diff --git a/.circleci/config.yml b/.circleci/config.yml index 0614f6975..8e2cc3e37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1055,7 +1055,7 @@ jobs: - run: command: | which pg_isready || sudo apt-get update && sudo apt-get install -y postgresql-client - make DOCKER_ARGS='-d' -C testing/dbtest/docker database-up + make DOCKER_ARGS='-d' PG_OPTS='-c shared_buffers=256MB -c max_connections=200000' -C testing/dbtest/docker database-up until pg_isready -h 127.0.0.1; do docker container inspect boundary-sql-tests &> /dev/null || exit -1; sleep 1; done name: Initialize Test Database - run: @@ -1267,6 +1267,17 @@ jobs: name: Save package cache paths: - .buildcache/packages/store + trigger-merge-to-ent: + machine: + image: ubuntu-2004:202107-02 + resource_class: medium + working_directory: ~/boundary + steps: + - checkout + - run: + command: | + ./scripts/trigger-merge-to-ent + name: Trigger Merge to Enterprise openbsd_amd64_package: docker: - image: docker.mirror.hashicorp.services/circleci/buildpack-deps @@ -1648,4 +1659,11 @@ workflows: branches: only: - stable-website + trigger-merge-to-ent: + jobs: + - trigger-merge-to-ent: + filters: + branches: + only: + - main version: 2 diff --git a/.circleci/config/jobs/build.yml b/.circleci/config/jobs/build.yml index 9efbe0e0c..08880157b 100644 --- a/.circleci/config/jobs/build.yml +++ b/.circleci/config/jobs/build.yml @@ -5,7 +5,7 @@ steps: name: "Initialize Test Database" command: | which pg_isready || sudo apt-get update && sudo apt-get install -y postgresql-client - make DOCKER_ARGS='-d' -C testing/dbtest/docker database-up + make DOCKER_ARGS='-d' PG_OPTS='-c shared_buffers=256MB -c max_connections=200000' -C testing/dbtest/docker database-up until pg_isready -h 127.0.0.1; do docker container inspect boundary-sql-tests &> /dev/null || exit -1; sleep 1; done - run: name: "Run Acceptance Tests" diff --git a/.circleci/config/jobs/trigger-merge-to-ent.yml b/.circleci/config/jobs/trigger-merge-to-ent.yml new file mode 100644 index 000000000..b976eab5c --- /dev/null +++ b/.circleci/config/jobs/trigger-merge-to-ent.yml @@ -0,0 +1,7 @@ +executor: go-machine-medium +steps: +- checkout +- run: + name: "Trigger Merge to Enterprise" + command: | + ./scripts/trigger-merge-to-ent diff --git a/.circleci/config/workflows/trigger-merge-to-ent.yml b/.circleci/config/workflows/trigger-merge-to-ent.yml new file mode 100644 index 000000000..12edc67ce --- /dev/null +++ b/.circleci/config/workflows/trigger-merge-to-ent.yml @@ -0,0 +1,6 @@ +jobs: + - trigger-merge-to-ent: + filters: + branches: + only: + - main diff --git a/.hooks/pre-push b/.hooks/pre-push new file mode 100755 index 000000000..1cb94652d --- /dev/null +++ b/.hooks/pre-push @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +remote="$1" +remote_url=$(git remote get-url $remote) + +if [[ $remote_url == *"boundary-enterprise"* ]]; then + exit 0 +fi + +if [ -f cmd/boundary/package_registration_ent.go ]; then + echo "Found enterprise file while pushing to oss remote" + exit 1 +fi + +exit 0 diff --git a/Makefile b/Makefile index e228e39a7..9b060bfef 100644 --- a/Makefile +++ b/Makefile @@ -127,6 +127,7 @@ protobuild: @protoc-go-inject-tag -input=./internal/kms/store/session_key.pb.go @protoc-go-inject-tag -input=./internal/kms/store/oidc_key.pb.go @protoc-go-inject-tag -input=./internal/target/store/target.pb.go + @protoc-go-inject-tag -input=./internal/target/tcp/store/target.pb.go @protoc-go-inject-tag -input=./internal/auth/oidc/store/oidc.pb.go @protoc-go-inject-tag -input=./internal/scheduler/job/store/job.pb.go @protoc-go-inject-tag -input=./internal/credential/store/credential.pb.go diff --git a/cmd/boundary/.gitignore b/cmd/boundary/.gitignore new file mode 100644 index 000000000..db02d202c --- /dev/null +++ b/cmd/boundary/.gitignore @@ -0,0 +1 @@ +/boundary diff --git a/cmd/boundary/package_registry_oss.go b/cmd/boundary/package_registry_oss.go new file mode 100644 index 000000000..65616ff18 --- /dev/null +++ b/cmd/boundary/package_registry_oss.go @@ -0,0 +1,6 @@ +package main + +import ( + // Enable tcp target support. + _ "github.com/hashicorp/boundary/internal/target/tcp" +) diff --git a/go.mod b/go.mod index c5528515d..af6584489 100644 --- a/go.mod +++ b/go.mod @@ -19,10 +19,12 @@ require ( github.com/fatih/color v1.12.0 github.com/fatih/structs v1.1.0 github.com/favadi/protoc-go-inject-tag v1.3.0 + github.com/godbus/dbus/v5 v5.0.4 // indirect github.com/golang-migrate/migrate/v4 v4.14.1 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe github.com/golang/protobuf v1.5.2 github.com/google/go-cmp v0.5.6 + github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0 github.com/hashicorp/boundary/api v0.0.19 github.com/hashicorp/boundary/sdk v0.0.12-0.20211007161358-65f3725bf3e1 diff --git a/go.sum b/go.sum index 6ee21fb45..ee9a46e93 100644 --- a/go.sum +++ b/go.sum @@ -341,8 +341,9 @@ github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/E github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.0 h1:MSdYClljsF3PbENUUEx85nkWfJSGfzYI9yEBZOJz6CY= github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -451,6 +452,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -1438,6 +1441,7 @@ google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= diff --git a/internal/auth/additional_verification_test.go b/internal/auth/additional_verification_test.go index a73c21f4b..2f0b6e633 100644 --- a/internal/auth/additional_verification_test.go +++ b/internal/auth/additional_verification_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/api/authtokens" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/servers/controller" @@ -92,10 +93,10 @@ func TestFetchActionSetForId(t *testing.T) { authTokenRepoFn, serversRepoFn, tc.Kms(), - auth.RequestInfo{ + &authpb.RequestInfo{ PublicId: token.Id, EncryptedToken: strings.Split(token.Token, "_")[2], - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), }) typ := resource.Target if tt.typeOverride != resource.Unknown { diff --git a/internal/auth/oidc/repository_auth_method.go b/internal/auth/oidc/repository_auth_method.go index da8dbb569..cadba1dd7 100644 --- a/internal/auth/oidc/repository_auth_method.go +++ b/internal/auth/oidc/repository_auth_method.go @@ -2,11 +2,9 @@ package oidc import ( "context" - "database/sql" "encoding/json" "fmt" "net/url" - "strings" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" @@ -72,148 +70,84 @@ func (r *Repository) upsertAccount(ctx context.Context, am *AuthMethod, IdTokenC if err != nil { return nil, errors.Wrap(ctx, err, op) } - - columns := []string{"public_id", "auth_method_id", "issuer", "subject"} - values := []interface{}{ - sql.Named("1", pubId), - sql.Named("2", am.PublicId), - sql.Named("3", iss), - sql.Named("4", sub), - } - var conflictClauses, fieldMasks, nullMasks []string - - { - marshaledTokenClaims, err := json.Marshal(IdTokenClaims) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - columns, values = append(columns, "token_claims"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), string(marshaledTokenClaims))) - conflictClauses = append(conflictClauses, fmt.Sprintf("token_claims = @%d", len(values))) - fieldMasks = append(fieldMasks, TokenClaimsField) - } - { - marshaledAccessTokenClaims, err := json.Marshal(AccessTokenClaims) - if err != nil { - return nil, errors.Wrap(ctx, err, op) - } - columns, values = append(columns, "userinfo_claims"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), string(marshaledAccessTokenClaims))) - conflictClauses = append(conflictClauses, fmt.Sprintf("userinfo_claims = @%d", len(values))) - fieldMasks = append(fieldMasks, UserinfoClaimsField) - } - issAsUrl, err := url.Parse(iss) if err != nil { return nil, errors.New(ctx, errors.Unknown, op, "unable to parse issuer", errors.WithWrap(err)) } - acctForOplog, err := NewAccount(ctx, am.PublicId, sub, WithIssuer(issAsUrl)) - if err != nil { - return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create new acct for oplog")) + + acct := AllocAccount() + acct.PublicId = pubId + acct.AuthMethodId = am.PublicId + acct.Issuer = issAsUrl.String() + acct.Subject = sub + + oc := db.OnConflict{ + Target: db.Constraint("auth_oidc_account_auth_method_id_issuer_subject_uq"), + Action: db.SetColumns([]string{"public_id", "auth_method_id", "issuer", "subject", "userinfo_claims", "token_claims"}), } - var foundName interface{} + marshaledTokenClaims, err := json.Marshal(IdTokenClaims) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + acct.TokenClaims = string(marshaledTokenClaims) + + marshaledAccessTokenClaims, err := json.Marshal(AccessTokenClaims) + if err != nil { + return nil, errors.Wrap(ctx, err, op) + } + acct.UserinfoClaims = string(marshaledAccessTokenClaims) + + var foundName, foundEmail bool switch { case AccessTokenClaims[fromName] != nil: - foundName = AccessTokenClaims[fromName] - columns, values = append(columns, "full_name"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), foundName)) + foundName = true + acct.FullName = AccessTokenClaims[fromName].(string) case IdTokenClaims[fromName] != nil: - foundName = IdTokenClaims[fromName] - columns, values = append(columns, "full_name"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), foundName)) - } - if foundName != nil { - acctForOplog.FullName = foundName.(string) - conflictClauses = append(conflictClauses, fmt.Sprintf("full_name = @%d", len(values))) - fieldMasks = append(fieldMasks, NameField) - } else { - conflictClauses = append(conflictClauses, "full_name = NULL") - nullMasks = append(nullMasks, NameField) + foundName = true + acct.FullName = IdTokenClaims[fromName].(string) } - var foundEmail interface{} switch { case AccessTokenClaims[fromEmail] != nil: - foundEmail = AccessTokenClaims[fromEmail] - columns, values = append(columns, "email"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), foundEmail)) + foundEmail = true + acct.Email = AccessTokenClaims[fromEmail].(string) case IdTokenClaims[fromEmail] != nil: - foundEmail = IdTokenClaims[fromEmail] - columns, values = append(columns, "email"), append(values, sql.Named(fmt.Sprintf("%d", len(values)+1), foundEmail)) + foundEmail = true + acct.Email = IdTokenClaims[fromEmail].(string) } - if foundEmail != nil { - acctForOplog.Email = foundEmail.(string) - conflictClauses = append(conflictClauses, fmt.Sprintf("email = @%d", len(values))) - fieldMasks = append(fieldMasks, "Email") - } else { - conflictClauses = append(conflictClauses, "email = NULL") - nullMasks = append(nullMasks, "Email") + if foundName { + oc.Action = append(oc.Action.([]db.ColumnValue), db.SetColumns([]string{"full_name"})...) } - - placeHolders := make([]string, 0, len(columns)) - for colNum := range columns { - placeHolders = append(placeHolders, fmt.Sprintf("@%d", colNum+1)) + if foundEmail { + oc.Action = append(oc.Action.([]db.ColumnValue), db.SetColumns([]string{"email"})...) } - query := fmt.Sprintf(acctUpsertQuery, strings.Join(columns, ", "), strings.Join(placeHolders, ", "), strings.Join(conflictClauses, ", ")) oplogWrapper, err := r.kms.GetWrapper(ctx, am.ScopeId, kms.KeyPurposeOplog) if err != nil { return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } - updatedAcct := AllocAccount() _, err = r.writer.DoTx( ctx, db.StdRetryCnt, db.ExpBackoff{}, func(reader db.Reader, w db.Writer) error { - var err error - rows, err := w.Query(ctx, query, values) + var rowCnt int64 + err := w.Create(ctx, acct, db.WithOnConflict(&oc), db.WithReturnRowsAffected(&rowCnt), db.WithOplog(oplogWrapper, acct.oplog(oplog.OpType_OP_TYPE_CREATE, am.ScopeId))) if err != nil { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to insert/update auth oidc account")) } - defer rows.Close() - result := struct { - PublicId string - Version int - }{} - var rowCnt int - for rows.Next() { - rowCnt += 1 - err = r.reader.ScanRows(rows, &result) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to scan rows for account")) - } - } if rowCnt > 1 { return errors.New(ctx, errors.MultipleRecords, op, fmt.Sprintf("expected 1 row but got: %d", rowCnt)) } - if err := reader.LookupWhere(ctx, &updatedAcct, "auth_method_id = ? and issuer = ? and subject = ?", am.PublicId, iss, sub); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("unable to look up auth oidc account for: %s / %s / %s", am.PublicId, iss, sub))) - } - // include the version incase of predictable account public ids based on a calculation using authmethod id and subject - if result.Version == 1 && updatedAcct.PublicId == pubId { - if err := upsertOplog(ctx, w, oplogWrapper, oplog.OpType_OP_TYPE_CREATE, am.ScopeId, updatedAcct, nil, nil); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write create oplog for account")) - } - } else { - if len(fieldMasks) > 0 || len(nullMasks) > 0 { - acctForOplog := AllocAccount() - acctForOplog.PublicId = updatedAcct.PublicId - if foundEmail != nil { - acctForOplog.Email = foundEmail.(string) - } - if foundName != nil { - acctForOplog.FullName = foundName.(string) - } - if err := upsertOplog(ctx, w, oplogWrapper, oplog.OpType_OP_TYPE_UPDATE, am.ScopeId, acctForOplog, fieldMasks, nullMasks); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write update oplog for account")) - } - } - } return nil }, ) if err != nil { return nil, errors.Wrap(ctx, err, op) } - return updatedAcct, nil + return acct, nil } // upsertOplog will write oplog msgs for account upserts. The db.Writer needs to be the writer for the current diff --git a/internal/auth/password/repository_account.go b/internal/auth/password/repository_account.go index 3bb78540f..4bd0beb07 100644 --- a/internal/auth/password/repository_account.go +++ b/internal/auth/password/repository_account.go @@ -212,7 +212,7 @@ func validLoginName(u string) bool { if u == "" { return false } - return !reInvalidLoginName.Match([]byte(u)) + return !reInvalidLoginName.MatchString(u) } // UpdateAccount updates the repository entry for a.PublicId with the diff --git a/internal/cmd/base/initial_resources.go b/internal/cmd/base/initial_resources.go index f57319ae0..d6d7ce65e 100644 --- a/internal/cmd/base/initial_resources.go +++ b/internal/cmd/base/initial_resources.go @@ -13,6 +13,7 @@ import ( hostplugin "github.com/hashicorp/boundary/internal/plugin/host" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/internal/types/scope" plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin" "github.com/hashicorp/go-secure-stdlib/base62" @@ -497,7 +498,7 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) // Host Catalog if b.DevTargetId == "" { - b.DevTargetId, err = db.NewPublicId(target.TcpTargetPrefix) + b.DevTargetId, err = db.NewPublicId(tcp.TargetPrefix) if err != nil { return nil, fmt.Errorf("error generating initial target id: %w", err) } @@ -514,11 +515,11 @@ func (b *Server) CreateInitialTarget(ctx context.Context) (target.Target, error) target.WithSessionConnectionLimit(int32(b.DevTargetSessionConnectionLimit)), target.WithPublicId(b.DevTargetId), } - t, err := target.NewTcpTarget(b.DevProjectId, opts...) + t, err := tcp.New(b.DevProjectId, opts...) if err != nil { return nil, fmt.Errorf("error creating in memory target: %w", err) } - tt, _, _, err := targetRepo.CreateTcpTarget(cancelCtx, t, opts...) + tt, _, _, err := targetRepo.CreateTarget(cancelCtx, t, opts...) if err != nil { return nil, fmt.Errorf("error saving target to the db: %w", err) } diff --git a/internal/cmd/commands/dev/dev.go b/internal/cmd/commands/dev/dev.go index f8816e6b1..b6de8cd7c 100644 --- a/internal/cmd/commands/dev/dev.go +++ b/internal/cmd/commands/dev/dev.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller" "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/worker" - "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/go-secure-stdlib/parseutil" "github.com/hashicorp/go-secure-stdlib/strutil" @@ -397,7 +397,7 @@ func (c *Command) Run(args []string) int { c.DevHostCatalogId = fmt.Sprintf("%s_%s", static.HostCatalogPrefix, c.flagIdSuffix) c.DevHostSetId = fmt.Sprintf("%s_%s", static.HostSetPrefix, c.flagIdSuffix) c.DevHostId = fmt.Sprintf("%s_%s", static.HostPrefix, c.flagIdSuffix) - c.DevTargetId = fmt.Sprintf("%s_%s", target.TcpTargetPrefix, c.flagIdSuffix) + c.DevTargetId = fmt.Sprintf("%s_%s", tcp.TargetPrefix, c.flagIdSuffix) } host, port, err := net.SplitHostPort(c.flagHostAddress) diff --git a/internal/cmd/common/flags.go b/internal/cmd/common/flags.go index f7b197896..9c022b383 100644 --- a/internal/cmd/common/flags.go +++ b/internal/cmd/common/flags.go @@ -301,7 +301,7 @@ func HandleAttributeFlags(c *base.Command, suffix, fullField string, sepFields [ case strings.HasPrefix(field.Value, `"`): // explicitly quoted string val = strings.Trim(field.Value, `"`) - case jsonNumberRegex.Match([]byte(strings.Trim(field.Value, `"`))): // number + case jsonNumberRegex.MatchString(strings.Trim(field.Value, `"`)): // number // Same logic as above if strings.Contains(field.Value, ".") { val, err = strconv.ParseFloat(field.Value, 64) diff --git a/internal/credential/vault/jobs_test.go b/internal/credential/vault/jobs_test.go index 3ceeaadf2..b6f16e73e 100644 --- a/internal/credential/vault/jobs_test.go +++ b/internal/credential/vault/jobs_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" wrapping "github.com/hashicorp/go-kms-wrapping" vault "github.com/hashicorp/vault/api" "github.com/stretchr/testify/assert" @@ -815,7 +816,7 @@ func TestTokenRevocationJob_Run(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -1008,7 +1009,7 @@ func TestCredentialRenewalJob_RunLimits(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -1117,7 +1118,7 @@ func TestCredentialRenewalJob_Run(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -1223,7 +1224,7 @@ func TestCredentialRenewalJob_RunExpired(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -1296,7 +1297,7 @@ func TestCredentialRenewalJob_NextRunIn(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -1535,7 +1536,7 @@ func TestCredentialRevocationJob_RunLimits(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, @@ -1646,7 +1647,7 @@ func TestCredentialRevocationJob_Run(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -1736,7 +1737,7 @@ func TestCredentialRevocationJob_RunDeleted(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), @@ -2114,7 +2115,7 @@ func TestCredentialCleanupJob_Run(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess1 := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: h.GetPublicId(), diff --git a/internal/credential/vault/repository_credentials_test.go b/internal/credential/vault/repository_credentials_test.go index 8c0604d02..20f9d1cc5 100644 --- a/internal/credential/vault/repository_credentials_test.go +++ b/internal/credential/vault/repository_credentials_test.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/boundary/internal/scheduler" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -103,7 +104,7 @@ func TestRepository_IssueCredentials(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) rc2dc := func(rcs []credential.Request) []*session.DynamicCredential { var dcs []*session.DynamicCredential @@ -221,7 +222,7 @@ func TestRepository_Revoke(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) const ( @@ -339,7 +340,7 @@ func Test_TerminateSession(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) const ( diff --git a/internal/credential/vault/testing_external_test.go b/internal/credential/vault/testing_external_test.go index ef7c17f45..4b24273eb 100644 --- a/internal/credential/vault/testing_external_test.go +++ b/internal/credential/vault/testing_external_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -34,7 +35,7 @@ func Test_TestCredentials(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) target.TestCredentialLibrary(t, conn, tar.GetPublicId(), cl.GetPublicId()) at := authtoken.TestAuthToken(t, conn, kms, org.GetPublicId()) diff --git a/internal/db/clause.go b/internal/db/clause.go new file mode 100644 index 000000000..0beb4d758 --- /dev/null +++ b/internal/db/clause.go @@ -0,0 +1,123 @@ +package db + +import ( + "sort" + + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +// ColumnValue defines a column and it's assigned value for a database operation +type ColumnValue struct { + column string + value interface{} +} + +type assigner interface { + toAssignment(column string) clause.Assignment +} + +// column represents a table column +type column struct { + name string + table string +} + +func (c *column) toAssignment(column string) clause.Assignment { + return clause.Assignment{ + Column: clause.Column{Name: column}, + Value: clause.Column{Table: c.table, Name: c.name}, + } +} + +func rawAssignment(column string, value interface{}) clause.Assignment { + return clause.Assignment{ + Column: clause.Column{Name: column}, + Value: value, + } +} + +// ExprValue encapsulates an expression value for a column assignment. See +// Expr(...) to create these values. +type ExprValue struct { + sql string + vars []interface{} +} + +func (ev *ExprValue) toAssignment(column string) clause.Assignment { + return clause.Assignment{ + Column: clause.Column{Name: column}, + Value: gorm.Expr(ev.sql, ev.vars...), + } +} + +// Expr creates an expression value (ExprValue) which can be used when setting +// column values for database operations. See: Expr(...) +// +// Set name column to null example: +// SetColumnValues(map[string]interface{}{"name": Expr("NULL")}) +// +// Set exp_time column to N seconds from now: +// SetColumnValues(map[string]interface{}{"exp_time": Expr("wt_add_seconds_to_now(?)", 10)}) +func Expr(expr string, args ...interface{}) ExprValue { + return ExprValue{sql: expr, vars: args} +} + +// SetColumnValues defines a map from column names to values +func SetColumnValues(columnValues map[string]interface{}) []ColumnValue { + keys := make([]string, 0, len(columnValues)) + for key := range columnValues { + keys = append(keys, key) + } + sort.Strings(keys) + + assignments := make([]ColumnValue, len(keys)) + for idx, key := range keys { + assignments[idx] = ColumnValue{column: key, value: columnValues[key]} + } + return assignments +} + +// SetColumns defines a list of column (names) to update using the set of +// proposed insert columns during an on conflict update. +func SetColumns(names []string) []ColumnValue { + assignments := make([]ColumnValue, len(names)) + for idx, name := range names { + assignments[idx] = ColumnValue{ + column: name, + value: column{name: name, table: "excluded"}, + } + } + return assignments +} + +// OnConflict specifies how to handle alternative actions to take when an insert +// results in a unique constraint or exclusion constraint error. +type OnConflict struct { + + // Target specifies what conflict you want to define a policy for. This can + // be any one of these: + // Columns: the name of a specific column or columns + // Constraint: the name of a unique constraint + Target interface{} + + // Action specifies the action to take on conflict. This can be any one of + // these: + // DoNothing: leaves the conflicting record as-is + // UpdateAll: updates all the columns of the conflicting record using the resource's data + // []ColumnValue: update a set of columns of the conflicting record using the set of assignments + Action interface{} +} + +// Constraint defines database constraint name +type Constraint string + +// Columns defines a set of column names +type Columns []string + +// DoNothing defines an "on conflict" action of doing nothing +type DoNothing bool + +// UpdateAll defines an "on conflict" action of updating all columns using the +// proposed insert column values +type UpdateAll bool diff --git a/internal/db/option.go b/internal/db/option.go index 32b50dcc4..ba963875d 100644 --- a/internal/db/option.go +++ b/internal/db/option.go @@ -47,6 +47,9 @@ type Options struct { withGormFormatter hclog.Logger withMaxOpenConnections int + + withOnConflict *OnConflict + withRowsAffected *int64 } type oplogOpts struct { @@ -185,3 +188,19 @@ func WithMaxOpenConnections(max int) Option { o.withMaxOpenConnections = max } } + +// WithOnConflict specifies an optional on conflict criteria which specify +// alternative actions to take when an insert results in a unique constraint or +// exclusion constraint error +func WithOnConflict(onConflict *OnConflict) Option { + return func(o *Options) { + o.withOnConflict = onConflict + } +} + +// WithReturnRowsAffected specifies an option for returning the rows affected +func WithReturnRowsAffected(rowsAffected *int64) Option { + return func(o *Options) { + o.withRowsAffected = rowsAffected + } +} diff --git a/internal/db/option_test.go b/internal/db/option_test.go index 963b9d008..f2cf9588b 100644 --- a/internal/db/option_test.go +++ b/internal/db/option_test.go @@ -141,8 +141,6 @@ func Test_getOpts(t *testing.T) { testOpts := getDefaultOptions() testOpts.withSkipVetForWrite = false assert.Equal(opts, testOpts) - - // try setting to false opts = GetOpts(WithSkipVetForWrite(true)) testOpts = getDefaultOptions() testOpts.withSkipVetForWrite = true @@ -156,8 +154,6 @@ func Test_getOpts(t *testing.T) { testOpts.withWhereClause = "" testOpts.withWhereClauseArgs = nil assert.Equal(opts, testOpts) - - // try setting to false opts = GetOpts(WithWhere("id = ? and foo = ?", 1234, "bar")) testOpts.withWhereClause = "id = ? and foo = ?" testOpts.withWhereClauseArgs = []interface{}{1234, "bar"} @@ -170,8 +166,6 @@ func Test_getOpts(t *testing.T) { testOpts := getDefaultOptions() testOpts.withOrder = "" assert.Equal(opts, testOpts) - - // try setting to false opts = GetOpts(WithOrder("version desc")) testOpts.withOrder = "version desc" assert.Equal(opts, testOpts) @@ -184,8 +178,6 @@ func Test_getOpts(t *testing.T) { assert.Equal(opts, testOpts) testLogger := hclog.New(&hclog.LoggerOptions{}) - - // try setting to false opts = GetOpts(WithGormFormatter(testLogger)) testOpts.withGormFormatter = testLogger assert.Equal(opts, testOpts) @@ -196,10 +188,37 @@ func Test_getOpts(t *testing.T) { opts := GetOpts() testOpts := getDefaultOptions() assert.Equal(opts, testOpts) - - // try setting to false opts = GetOpts(WithMaxOpenConnections(22)) testOpts.withMaxOpenConnections = 22 assert.Equal(opts, testOpts) }) + t.Run("WithOnConflict", func(t *testing.T) { + assert := assert.New(t) + // test default of false + opts := GetOpts() + testOpts := getDefaultOptions() + assert.Equal(opts, testOpts) + + columns := SetColumns([]string{"name", "description"}) + columnValues := SetColumnValues(map[string]interface{}{"expiration": "NULL"}) + testOnConflict := OnConflict{ + Target: Constraint("uniq-name"), + Action: append(columns, columnValues...), + } + opts = GetOpts(WithOnConflict(&testOnConflict)) + testOpts.withOnConflict = &testOnConflict + assert.Equal(opts, testOpts) + }) + t.Run("WithReturnRowsAffected", func(t *testing.T) { + assert := assert.New(t) + // test default of false + opts := GetOpts() + testOpts := getDefaultOptions() + assert.Equal(opts, testOpts) + + var rowsAffected int64 + opts = GetOpts(WithReturnRowsAffected(&rowsAffected)) + testOpts.withRowsAffected = &rowsAffected + assert.Equal(opts, testOpts) + }) } diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 119bb8f42..e942147ae 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -15,6 +15,7 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "google.golang.org/protobuf/proto" "gorm.io/gorm" + "gorm.io/gorm/clause" ) const ( @@ -260,10 +261,12 @@ func (rw *Db) lookupAfterWrite(ctx context.Context, i interface{}, opt ...Option return nil } -// Create an object in the db with options: WithOplog, NewOplogMsg and -// WithLookup. WithOplog will write an oplog entry for the create. +// Create an object in the db with options: WithOplog, NewOplogMsg, WithLookup +// and OnConflict. WithOplog will write an oplog entry for the create. // NewOplogMsg will return in-memory oplog message. WithOplog and NewOplogMsg // cannot be used together. WithLookup with to force a lookup after create. +// OnConflict specifies alternative actions to take when an insert results in a +// unique constraint or exclusion constraint error. func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error { const op = "db.Create" if rw.underlying == nil { @@ -295,6 +298,54 @@ func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error { } } } + + db := rw.underlying.WithContext(ctx) + var onConflictDoNothing bool + if opts.withOnConflict != nil { + c := clause.OnConflict{} + switch opts.withOnConflict.Target.(type) { + case Constraint: + c.OnConstraint = string(opts.withOnConflict.Target.(Constraint)) + case Columns: + columns := make([]clause.Column, 0, len(opts.withOnConflict.Target.(Columns))) + for _, name := range opts.withOnConflict.Target.(Columns) { + columns = append(columns, clause.Column{Name: name}) + } + c.Columns = columns + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("invalid conflict target: %v", reflect.TypeOf(opts.withOnConflict.Target))) + } + + switch opts.withOnConflict.Action.(type) { + case DoNothing: + c.DoNothing = true + onConflictDoNothing = true + case UpdateAll: + c.UpdateAll = true + case []ColumnValue: + updates := opts.withOnConflict.Action.([]ColumnValue) + set := make(clause.Set, 0, len(updates)) + for _, s := range updates { + // make sure it's not one of the std immutable columns + if contains([]string{"createtime", "publicid"}, strings.ToLower(s.column)) { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("Cannot do update on conflict for column %s", s.column)) + } + switch sv := s.value.(type) { + case column: + set = append(set, sv.toAssignment(s.column)) + case ExprValue: + set = append(set, sv.toAssignment(s.column)) + default: + set = append(set, rawAssignment(s.column, s.value)) + } + } + c.DoUpdates = set + default: + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("invalid conflict action: %v", reflect.TypeOf(opts.withOnConflict.Action))) + } + db = db.Clauses(c) + } + var ticket *store.Ticket if withOplog { var err error @@ -303,20 +354,32 @@ func (rw *Db) Create(ctx context.Context, i interface{}, opt ...Option) error { return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) } } - if err := rw.underlying.Create(i).Error; err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("create failed"), errors.WithoutEvent()) + tx := db.Create(i) + if tx.Error != nil { + return errors.Wrap(ctx, tx.Error, op, errors.WithMsg("create failed"), errors.WithoutEvent()) + } + if opts.withRowsAffected != nil { + *opts.withRowsAffected = tx.RowsAffected } if withOplog { - if err := rw.addOplog(ctx, CreateOp, opts, ticket, i); err != nil { - return errors.Wrap(ctx, err, op) + switch { + case onConflictDoNothing && tx.RowsAffected == 0: + default: + if err := rw.addOplog(ctx, CreateOp, opts, ticket, i); err != nil { + return errors.Wrap(ctx, err, op) + } } } if opts.newOplogMsg != nil { - msg, err := rw.newOplogMessage(ctx, CreateOp, i) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("returning oplog failed")) + switch { + case onConflictDoNothing && tx.RowsAffected == 0: + default: + msg, err := rw.newOplogMessage(ctx, CreateOp, i) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("returning oplog failed")) + } + *opts.newOplogMsg = *msg } - *opts.newOplogMsg = *msg } if err := rw.lookupAfterWrite(ctx, i, opt...); err != nil { return errors.Wrap(ctx, err, op, errors.WithoutEvent()) diff --git a/internal/db/read_writer_ext_test.go b/internal/db/read_writer_ext_test.go new file mode 100644 index 000000000..889ecdaaa --- /dev/null +++ b/internal/db/read_writer_ext_test.go @@ -0,0 +1,152 @@ +package db_test + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/db_test" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" +) + +func TestDb_Create_OnConflict(t *testing.T) { + ctx := context.Background() + wrapper := db.TestWrapper(t) + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + + // create initial user for on conflict tests + id, err := db.NewPublicId("test-user") + require.NoError(t, err) + initialUser, err := db_test.NewTestUser() + require.NoError(t, err) + ts := ×tamp.Timestamp{Timestamp: timestamppb.Now()} + initialUser.CreateTime = ts + initialUser.UpdateTime = ts + initialUser.Name = "foo-" + id + err = rw.Create(ctx, initialUser) + require.NoError(t, err) + assert.NotEmpty(t, initialUser.Id) + + tests := []struct { + name string + onConflict db.OnConflict + wantUpdate bool + wantEmail string + wantOplog bool + }{ + { + name: "set-columns", + onConflict: db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: db.SetColumns([]string{"name"}), + }, + wantUpdate: true, + wantOplog: true, + }, + { + name: "set-column-values", + onConflict: db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: db.SetColumnValues(map[string]interface{}{ + "name": db.Expr("md5(?)", "alice eve smith"), + "email": "alice@gmail.com", + "phone_number": db.Expr("NULL"), + }), + }, + wantUpdate: true, + wantEmail: "alice@gmail.com", + wantOplog: true, + }, + { + name: "both-set-columns-and-set-column-values", + onConflict: func() db.OnConflict { + onConflict := db.OnConflict{ + Target: db.Columns{"public_id"}, + } + cv := db.SetColumns([]string{"name"}) + cv = append(cv, + db.SetColumnValues(map[string]interface{}{ + "email": "alice@gmail.com", + "phone_number": db.Expr("NULL"), + })...) + onConflict.Action = cv + return onConflict + }(), + wantUpdate: true, + wantEmail: "alice@gmail.com", + wantOplog: true, + }, + { + name: "update-all", + onConflict: db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: db.UpdateAll(true), + }, + wantUpdate: true, + wantOplog: true, + }, + { + name: "do-nothing", + onConflict: db.OnConflict{ + Target: db.Columns{"public_id"}, + Action: db.DoNothing(true), + }, + wantOplog: false, + }, + { + name: "on-constraint", + onConflict: db.OnConflict{ + Target: db.Constraint("db_test_user_public_id_key"), + Action: db.SetColumns([]string{"name"}), + }, + wantUpdate: true, + wantOplog: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + conflictUser, err := db_test.NewTestUser() + require.NoError(err) + userNameId, err := db.NewPublicId("test-user-name") + require.NoError(err) + conflictUser.PublicId = initialUser.PublicId + conflictUser.Name = userNameId + md := oplog.Metadata{ + "resource-public-id": []string{conflictUser.PublicId}, + "op-type": []string{oplog.OpType_OP_TYPE_CREATE.String()}, + } + var rowsAffected int64 + err = rw.Create(ctx, conflictUser, db.WithOnConflict(&tt.onConflict), db.WithOplog(wrapper, md), db.WithReturnRowsAffected(&rowsAffected)) + require.NoError(err) + if tt.wantOplog { + err = db.TestVerifyOplog(t, rw, conflictUser.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)) + assert.NoError(err) + } + foundUser, err := db_test.NewTestUser() + require.NoError(err) + foundUser.PublicId = conflictUser.PublicId + err = rw.LookupByPublicId(context.Background(), foundUser) + require.NoError(err) + if tt.wantUpdate { + assert.Equal(int64(1), rowsAffected) + assert.Equal(conflictUser.Id, foundUser.Id) + assert.Equal(conflictUser.Name, foundUser.Name) + if tt.wantEmail != "" { + assert.Equal(tt.wantEmail, foundUser.Email) + } + } else { + assert.Equal(int64(0), rowsAffected) + assert.NotEqual(conflictUser.Id, foundUser.Id) + assert.NotEqual(conflictUser.Name, foundUser.Name) + } + }) + } +} diff --git a/internal/db/read_writer_test.go b/internal/db/read_writer_test.go index 14404793c..793663dd4 100644 --- a/internal/db/read_writer_test.go +++ b/internal/db/read_writer_test.go @@ -723,8 +723,8 @@ func TestDb_Create(t *testing.T) { metadata := oplog.Metadata{ "resource-public-id": []string{user.PublicId}, - // "op-type": []string{oplog.OpType_OP_TYPE_UPDATE.String()}, } + err = w.WriteOplogEntryWith(context.Background(), TestWrapper(t), ticket, metadata, []*oplog.Message{&createMsg, &updateMsg}) require.NoError(err) diff --git a/internal/db/schema/migrations/oss/postgres/17/01_servers.up.sql b/internal/db/schema/migrations/oss/postgres/17/01_servers.up.sql new file mode 100644 index 000000000..4b84e5c9c --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/17/01_servers.up.sql @@ -0,0 +1,16 @@ +begin; + +-- server table inserts/updates historically included setting the update_time +-- column to now(). Since we're now using the "standard" db package to do this, +-- we need to add triggers to set the update_time for every insert/update. +create trigger + update_time_column +before update on server + for each row execute procedure update_time_column(); + +create trigger + insert_time_column +before insert on server + for each row execute procedure update_time_column(); + +commit; \ No newline at end of file diff --git a/internal/db/schema/migrations/oss/postgres_14_01_test.go b/internal/db/schema/migrations/oss/postgres_14_01_test.go index 3483a078a..fcc5421b2 100644 --- a/internal/db/schema/migrations/oss/postgres_14_01_test.go +++ b/internal/db/schema/migrations/oss/postgres_14_01_test.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/testing/dbtest" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" @@ -79,7 +80,7 @@ func TestMigrations_UserDimension(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) var sessions []*session.Session kmsCache := kms.TestKms(t, conn, wrapper) diff --git a/internal/db/schema/migrations/oss/postgres_16_05_test.go b/internal/db/schema/migrations/oss/postgres_16_05_test.go index f2d960738..58cbda927 100644 --- a/internal/db/schema/migrations/oss/postgres_16_05_test.go +++ b/internal/db/schema/migrations/oss/postgres_16_05_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/testing/dbtest" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/stretchr/testify/assert" @@ -81,7 +82,7 @@ func TestMigrations_CredentialDimension(t *testing.T) { h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) var sessions []*session.Session kmsCache := kms.TestKms(t, conn, wrapper) @@ -157,12 +158,12 @@ func TestMigrations_CredentialDimension(t *testing.T) { require.Equal(want, state) } -func testSessionCredentialParams(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper, tar *target.TcpTarget) []*session.DynamicCredential { +func testSessionCredentialParams(t *testing.T, conn *db.DB, kms *kms.Kms, wrapper wrapping.Wrapper, tar target.Target) []*session.DynamicCredential { t.Helper() rw := db.New(conn) ctx := context.Background() - stores := vault.TestCredentialStores(t, conn, wrapper, tar.ScopeId, 1) + stores := vault.TestCredentialStores(t, conn, wrapper, tar.GetScopeId(), 1) libs := vault.TestCredentialLibraries(t, conn, wrapper, stores[0].GetPublicId(), 2) targetRepo, err := target.NewRepository(rw, rw, kms) diff --git a/internal/errors/code.go b/internal/errors/code.go index bc4c254cf..02ea814ec 100644 --- a/internal/errors/code.go +++ b/internal/errors/code.go @@ -66,9 +66,11 @@ const ( GenKey Code = 304 // GenKey represents an error occurred during the underlying key generation process GenCert Code = 305 // GenCert represents an error occurred during the underlying certificate generation process - // General system errors are reserved Codes 400-599 and align with http client and server error codes - Forbidden Code = 403 // Forbidden represents the operation is forbidden - Internal Code = 500 // InternalError represents the system encountered an unexpected condition. + // General system errors are reserved Codes 400-599 and align with http + // client and server error codes + Unauthorized Code = 401 // Unauthorized represents the operation is unauthorized + Forbidden Code = 403 // Forbidden represents the operation is forbidden + Internal Code = 500 // InternalError represents the system encountered an unexpected condition. // DB errors are reserved Codes from 1000-1999 CheckConstraint Code = 1000 // CheckConstraint represents a check constraint error diff --git a/internal/errors/error.go b/internal/errors/error.go index c729f9e80..a4fdf3f99 100644 --- a/internal/errors/error.go +++ b/internal/errors/error.go @@ -7,6 +7,7 @@ import ( "runtime" "strings" + pberrors "github.com/hashicorp/boundary/internal/gen/errors" "github.com/hashicorp/boundary/internal/observability/event" "github.com/jackc/pgconn" ) @@ -266,6 +267,49 @@ func (e *Err) Error() string { return s.String() } +// ToPbErrors will convert to an Err protobuf +func ToPbErrors(err *Err) *pberrors.Err { + pbErr := &pberrors.Err{ + Code: uint32(err.Code), + Msg: err.Msg, + Op: string(err.Op), + } + + var wrappedErr *Err + isWrappedErr := As(err.Wrapped, &wrappedErr) + switch { + case err.Wrapped == nil: + pbErr.Wrapped = &pberrors.Err_None{ + None: false, + } + case isWrappedErr: + pbErr.Wrapped = &pberrors.Err_Err{ + Err: ToPbErrors(wrappedErr), + } + default: + pbErr.Wrapped = &pberrors.Err_StdError{ + StdError: err.Wrapped.Error(), + } + } + return pbErr +} + +// FromPbErrors will convert from Err protobuf +func FromPbErrors(pbErr *pberrors.Err) *Err { + err := &Err{ + Code: Code(pbErr.Code), + Msg: pbErr.Msg, + Op: Op(pbErr.Op), + } + switch w := pbErr.Wrapped.(type) { + case *pberrors.Err_Err: + err.Wrapped = FromPbErrors(w.Err) + case *pberrors.Err_StdError: + err.Wrapped = errors.New(w.StdError) + } + return err +} + func join(str *strings.Builder, delim string, s string) { if str.Len() == 0 { _, _ = str.WriteString(s) diff --git a/internal/errors/info.go b/internal/errors/info.go index 3d832623a..c9ec7a77c 100644 --- a/internal/errors/info.go +++ b/internal/errors/info.go @@ -148,6 +148,10 @@ var errorCodeInfo = map[Code]Info{ Message: "forbidden", Kind: Other, }, + Unauthorized: { + Message: "unauthorized", + Kind: Other, + }, CheckConstraint: { Message: "constraint check failed", Kind: Integrity, diff --git a/internal/gen/controller.swagger.json b/internal/gen/controller.swagger.json index 1f3125ceb..8b5f11ef0 100644 --- a/internal/gen/controller.swagger.json +++ b/internal/gen/controller.swagger.json @@ -5038,7 +5038,8 @@ "items": { "type": "string" }, - "description": "The IDs of the Host Sets associated with this Target." + "description": "Output only. The IDs of the Host Sets associated with this Target.", + "readOnly": true }, "host_sets": { "type": "array", @@ -5053,7 +5054,8 @@ "items": { "type": "string" }, - "description": "The IDs of the Host Sources associated with this Target." + "description": "Output only. The IDs of the Host Sources associated with this Target.", + "readOnly": true }, "host_sources": { "type": "array", @@ -5082,7 +5084,8 @@ "items": { "type": "string" }, - "description": "The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead." + "description": "Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead.", + "readOnly": true }, "application_credential_libraries": { "type": "array", @@ -5097,7 +5100,8 @@ "items": { "type": "string" }, - "description": "The IDs of the application credential source ids associated with this Target." + "description": "Output only. The IDs of the application credential source ids associated with this Target.", + "readOnly": true }, "application_credential_sources": { "type": "array", diff --git a/internal/gen/controller/api/empty_msg.pb.go b/internal/gen/controller/api/empty_msg.pb.go new file mode 100644 index 000000000..340e4d468 --- /dev/null +++ b/internal/gen/controller/api/empty_msg.pb.go @@ -0,0 +1,137 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: controller/api/v1/empty_msg.proto + +package api + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// EmptyResponse represents an empty response +type EmptyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyResponse) Reset() { + *x = EmptyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_api_v1_empty_msg_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EmptyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyResponse) ProtoMessage() {} + +func (x *EmptyResponse) ProtoReflect() protoreflect.Message { + mi := &file_controller_api_v1_empty_msg_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead. +func (*EmptyResponse) Descriptor() ([]byte, []int) { + return file_controller_api_v1_empty_msg_proto_rawDescGZIP(), []int{0} +} + +var File_controller_api_v1_empty_msg_proto protoreflect.FileDescriptor + +var file_controller_api_v1_empty_msg_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x6d, 0x73, 0x67, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x22, 0x0f, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_api_v1_empty_msg_proto_rawDescOnce sync.Once + file_controller_api_v1_empty_msg_proto_rawDescData = file_controller_api_v1_empty_msg_proto_rawDesc +) + +func file_controller_api_v1_empty_msg_proto_rawDescGZIP() []byte { + file_controller_api_v1_empty_msg_proto_rawDescOnce.Do(func() { + file_controller_api_v1_empty_msg_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_api_v1_empty_msg_proto_rawDescData) + }) + return file_controller_api_v1_empty_msg_proto_rawDescData +} + +var file_controller_api_v1_empty_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_api_v1_empty_msg_proto_goTypes = []interface{}{ + (*EmptyResponse)(nil), // 0: controller.api.v1.EmptyResponse +} +var file_controller_api_v1_empty_msg_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_api_v1_empty_msg_proto_init() } +func file_controller_api_v1_empty_msg_proto_init() { + if File_controller_api_v1_empty_msg_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_api_v1_empty_msg_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EmptyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_api_v1_empty_msg_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_api_v1_empty_msg_proto_goTypes, + DependencyIndexes: file_controller_api_v1_empty_msg_proto_depIdxs, + MessageInfos: file_controller_api_v1_empty_msg_proto_msgTypes, + }.Build() + File_controller_api_v1_empty_msg_proto = out.File + file_controller_api_v1_empty_msg_proto_rawDesc = nil + file_controller_api_v1_empty_msg_proto_goTypes = nil + file_controller_api_v1_empty_msg_proto_depIdxs = nil +} diff --git a/internal/gen/controller/auth/auth.pb.go b/internal/gen/controller/auth/auth.pb.go new file mode 100644 index 000000000..103a2360a --- /dev/null +++ b/internal/gen/controller/auth/auth.pb.go @@ -0,0 +1,263 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: controller/auth/v1/auth.proto + +package auth + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// RequestInfo contains request parameters necessary for checking authn/authz +type RequestInfo struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // path of the request + Path string `protobuf:"bytes,10,opt,name=path,proto3" json:"path,omitempty"` + // method (http verb) of the request + Method string `protobuf:"bytes,20,opt,name=method,proto3" json:"method,omitempty"` + // public_id for the request + PublicId string `protobuf:"bytes,30,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty"` + // encrypted_token from the request + EncryptedToken string `protobuf:"bytes,40,opt,name=encrypted_token,json=encryptedToken,proto3" json:"encrypted_token,omitempty"` + // token from the request + Token string `protobuf:"bytes,50,opt,name=token,proto3" json:"token,omitempty"` + // token_format of the request's token + TokenFormat uint32 `protobuf:"varint,60,opt,name=token_format,json=tokenFormat,proto3" json:"token_format,omitempty"` + // scope_id_override for the request (helpful for tests) + ScopeIdOverride string `protobuf:"bytes,70,opt,name=scope_id_override,json=scopeIdOverride,proto3" json:"scope_id_override,omitempty"` + // user_id_override for the request (helpful for tests) + UserIdOverride string `protobuf:"bytes,80,opt,name=user_id_override,json=userIdOverride,proto3" json:"user_id_override,omitempty"` + // disable_authz_failures for the request (helpful for tests) + DisableAuthzFailures bool `protobuf:"varint,90,opt,name=disable_authz_failures,json=disableAuthzFailures,proto3" json:"disable_authz_failures,omitempty"` + // disable_auth_entirely for the request (helpful for tests) + DisableAuthEntirely bool `protobuf:"varint,100,opt,name=disable_auth_entirely,json=disableAuthEntirely,proto3" json:"disable_auth_entirely,omitempty"` + // ticket is a unique id that allows the grpc-gateway to verify that the info + // came from its companion http proxy + Ticket string `protobuf:"bytes,110,opt,name=ticket,proto3" json:"ticket,omitempty"` +} + +func (x *RequestInfo) Reset() { + *x = RequestInfo{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_auth_v1_auth_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RequestInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestInfo) ProtoMessage() {} + +func (x *RequestInfo) ProtoReflect() protoreflect.Message { + mi := &file_controller_auth_v1_auth_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequestInfo.ProtoReflect.Descriptor instead. +func (*RequestInfo) Descriptor() ([]byte, []int) { + return file_controller_auth_v1_auth_proto_rawDescGZIP(), []int{0} +} + +func (x *RequestInfo) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *RequestInfo) GetMethod() string { + if x != nil { + return x.Method + } + return "" +} + +func (x *RequestInfo) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *RequestInfo) GetEncryptedToken() string { + if x != nil { + return x.EncryptedToken + } + return "" +} + +func (x *RequestInfo) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *RequestInfo) GetTokenFormat() uint32 { + if x != nil { + return x.TokenFormat + } + return 0 +} + +func (x *RequestInfo) GetScopeIdOverride() string { + if x != nil { + return x.ScopeIdOverride + } + return "" +} + +func (x *RequestInfo) GetUserIdOverride() string { + if x != nil { + return x.UserIdOverride + } + return "" +} + +func (x *RequestInfo) GetDisableAuthzFailures() bool { + if x != nil { + return x.DisableAuthzFailures + } + return false +} + +func (x *RequestInfo) GetDisableAuthEntirely() bool { + if x != nil { + return x.DisableAuthEntirely + } + return false +} + +func (x *RequestInfo) GetTicket() string { + if x != nil { + return x.Ticket + } + return "" +} + +var File_controller_auth_v1_auth_proto protoreflect.FileDescriptor + +var file_controller_auth_v1_auth_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x61, 0x75, 0x74, + 0x68, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x12, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x61, 0x75, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x22, 0x90, 0x03, 0x0a, 0x0b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, + 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x1e, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x28, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x32, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x74, + 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x2a, + 0x0a, 0x11, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, + 0x69, 0x64, 0x65, 0x18, 0x46, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x49, 0x64, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x50, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x4f, 0x76, 0x65, 0x72, + 0x72, 0x69, 0x64, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x7a, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x18, 0x5a, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x74, + 0x68, 0x7a, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x72, + 0x65, 0x6c, 0x79, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x64, 0x69, 0x73, 0x61, 0x62, + 0x6c, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x72, 0x65, 0x6c, 0x79, 0x12, 0x16, + 0x0a, 0x06, 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x69, 0x63, 0x6b, 0x65, 0x74, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x75, 0x74, 0x68, 0x3b, 0x61, 0x75, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_controller_auth_v1_auth_proto_rawDescOnce sync.Once + file_controller_auth_v1_auth_proto_rawDescData = file_controller_auth_v1_auth_proto_rawDesc +) + +func file_controller_auth_v1_auth_proto_rawDescGZIP() []byte { + file_controller_auth_v1_auth_proto_rawDescOnce.Do(func() { + file_controller_auth_v1_auth_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_auth_v1_auth_proto_rawDescData) + }) + return file_controller_auth_v1_auth_proto_rawDescData +} + +var file_controller_auth_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_auth_v1_auth_proto_goTypes = []interface{}{ + (*RequestInfo)(nil), // 0: controller.auth.v1.RequestInfo +} +var file_controller_auth_v1_auth_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_controller_auth_v1_auth_proto_init() } +func file_controller_auth_v1_auth_proto_init() { + if File_controller_auth_v1_auth_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_auth_v1_auth_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RequestInfo); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_auth_v1_auth_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_auth_v1_auth_proto_goTypes, + DependencyIndexes: file_controller_auth_v1_auth_proto_depIdxs, + MessageInfos: file_controller_auth_v1_auth_proto_msgTypes, + }.Build() + File_controller_auth_v1_auth_proto = out.File + file_controller_auth_v1_auth_proto_rawDesc = nil + file_controller_auth_v1_auth_proto_goTypes = nil + file_controller_auth_v1_auth_proto_depIdxs = nil +} diff --git a/internal/gen/errors/errors.pb.go b/internal/gen/errors/errors.pb.go new file mode 100644 index 000000000..80d525d88 --- /dev/null +++ b/internal/gen/errors/errors.pb.go @@ -0,0 +1,322 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: errors/v1/errors.proto + +package errors + +import ( + api "github.com/hashicorp/boundary/internal/gen/controller/api" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// Err is a protobuf representation of a domain error +type Err struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // code is the error's code, which can be used to get the error's + // errorCodeInfo, which contains the error's Kind and Message + Code uint32 `protobuf:"varint,10,opt,name=code,proto3" json:"code,omitempty"` + // msg for the error + Msg string `protobuf:"bytes,20,opt,name=msg,proto3" json:"msg,omitempty"` + // op represents the operation raising/propagating an error and is optional. + Op string `protobuf:"bytes,30,opt,name=op,proto3" json:"op,omitempty"` + // wrapped represents an optionally wrapped error + // + // Types that are assignable to Wrapped: + // *Err_Err + // *Err_StdError + // *Err_None + Wrapped isErr_Wrapped `protobuf_oneof:"wrapped"` +} + +func (x *Err) Reset() { + *x = Err{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_v1_errors_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Err) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Err) ProtoMessage() {} + +func (x *Err) ProtoReflect() protoreflect.Message { + mi := &file_errors_v1_errors_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Err.ProtoReflect.Descriptor instead. +func (*Err) Descriptor() ([]byte, []int) { + return file_errors_v1_errors_proto_rawDescGZIP(), []int{0} +} + +func (x *Err) GetCode() uint32 { + if x != nil { + return x.Code + } + return 0 +} + +func (x *Err) GetMsg() string { + if x != nil { + return x.Msg + } + return "" +} + +func (x *Err) GetOp() string { + if x != nil { + return x.Op + } + return "" +} + +func (m *Err) GetWrapped() isErr_Wrapped { + if m != nil { + return m.Wrapped + } + return nil +} + +func (x *Err) GetErr() *Err { + if x, ok := x.GetWrapped().(*Err_Err); ok { + return x.Err + } + return nil +} + +func (x *Err) GetStdError() string { + if x, ok := x.GetWrapped().(*Err_StdError); ok { + return x.StdError + } + return "" +} + +func (x *Err) GetNone() bool { + if x, ok := x.GetWrapped().(*Err_None); ok { + return x.None + } + return false +} + +type isErr_Wrapped interface { + isErr_Wrapped() +} + +type Err_Err struct { + // err is a wrapped Err (aka a domain error) + Err *Err `protobuf:"bytes,40,opt,name=err,proto3,oneof"` +} + +type Err_StdError struct { + // std_error is a wrapped std error + StdError string `protobuf:"bytes,50,opt,name=std_error,json=stdError,proto3,oneof"` +} + +type Err_None struct { + // no error or err is wrapped + None bool `protobuf:"varint,60,opt,name=none,proto3,oneof"` +} + +func (*Err_Err) isErr_Wrapped() {} + +func (*Err_StdError) isErr_Wrapped() {} + +func (*Err_None) isErr_Wrapped() {} + +// ApiError is a protobuf representation of a controller ApiError +type ApiError struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // api_error is the controller ApiError + ApiError *api.Error `protobuf:"bytes,10,opt,name=api_error,json=apiError,proto3" json:"api_error,omitempty"` + // status is the status code assocatiated with the api error + Status int32 `protobuf:"varint,20,opt,name=status,proto3" json:"status,omitempty"` +} + +func (x *ApiError) Reset() { + *x = ApiError{} + if protoimpl.UnsafeEnabled { + mi := &file_errors_v1_errors_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ApiError) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApiError) ProtoMessage() {} + +func (x *ApiError) ProtoReflect() protoreflect.Message { + mi := &file_errors_v1_errors_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApiError.ProtoReflect.Descriptor instead. +func (*ApiError) Descriptor() ([]byte, []int) { + return file_errors_v1_errors_proto_rawDescGZIP(), []int{1} +} + +func (x *ApiError) GetApiError() *api.Error { + if x != nil { + return x.ApiError + } + return nil +} + +func (x *ApiError) GetStatus() int32 { + if x != nil { + return x.Status + } + return 0 +} + +var File_errors_v1_errors_proto protoreflect.FileDescriptor + +var file_errors_v1_errors_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, + 0x2e, 0x76, 0x31, 0x1a, 0x1d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x9f, 0x01, 0x0a, 0x03, 0x45, 0x72, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, + 0x64, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, + 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x6f, 0x70, + 0x12, 0x22, 0x0a, 0x03, 0x65, 0x72, 0x72, 0x18, 0x28, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x48, 0x00, 0x52, + 0x03, 0x65, 0x72, 0x72, 0x12, 0x1d, 0x0a, 0x09, 0x73, 0x74, 0x64, 0x5f, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x32, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, 0x74, 0x64, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x64, 0x22, 0x59, 0x0a, 0x08, 0x41, 0x70, 0x69, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x35, 0x0a, 0x09, 0x61, 0x70, 0x69, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x08, 0x61, + 0x70, 0x69, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, + 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_errors_v1_errors_proto_rawDescOnce sync.Once + file_errors_v1_errors_proto_rawDescData = file_errors_v1_errors_proto_rawDesc +) + +func file_errors_v1_errors_proto_rawDescGZIP() []byte { + file_errors_v1_errors_proto_rawDescOnce.Do(func() { + file_errors_v1_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_v1_errors_proto_rawDescData) + }) + return file_errors_v1_errors_proto_rawDescData +} + +var file_errors_v1_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_errors_v1_errors_proto_goTypes = []interface{}{ + (*Err)(nil), // 0: errors.v1.Err + (*ApiError)(nil), // 1: errors.v1.ApiError + (*api.Error)(nil), // 2: controller.api.v1.Error +} +var file_errors_v1_errors_proto_depIdxs = []int32{ + 0, // 0: errors.v1.Err.err:type_name -> errors.v1.Err + 2, // 1: errors.v1.ApiError.api_error:type_name -> controller.api.v1.Error + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_errors_v1_errors_proto_init() } +func file_errors_v1_errors_proto_init() { + if File_errors_v1_errors_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_errors_v1_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Err); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_errors_v1_errors_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ApiError); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_errors_v1_errors_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Err_Err)(nil), + (*Err_StdError)(nil), + (*Err_None)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_errors_v1_errors_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_errors_v1_errors_proto_goTypes, + DependencyIndexes: file_errors_v1_errors_proto_depIdxs, + MessageInfos: file_errors_v1_errors_proto_msgTypes, + }.Build() + File_errors_v1_errors_proto = out.File + file_errors_v1_errors_proto_rawDesc = nil + file_errors_v1_errors_proto_goTypes = nil + file_errors_v1_errors_proto_depIdxs = nil +} diff --git a/internal/gen/testing/interceptor/greeter.pb.go b/internal/gen/testing/interceptor/greeter.pb.go new file mode 100644 index 000000000..a4b9df67f --- /dev/null +++ b/internal/gen/testing/interceptor/greeter.pb.go @@ -0,0 +1,222 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: testing/interceptor/v1/greeter.proto + +package interceptor + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// The request message containing the user's name. +type SayHelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *SayHelloRequest) Reset() { + *x = SayHelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_testing_interceptor_v1_greeter_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SayHelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SayHelloRequest) ProtoMessage() {} + +func (x *SayHelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_testing_interceptor_v1_greeter_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SayHelloRequest.ProtoReflect.Descriptor instead. +func (*SayHelloRequest) Descriptor() ([]byte, []int) { + return file_testing_interceptor_v1_greeter_proto_rawDescGZIP(), []int{0} +} + +func (x *SayHelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// The response message containing the greetings +type SayHelloResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *SayHelloResponse) Reset() { + *x = SayHelloResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_testing_interceptor_v1_greeter_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SayHelloResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SayHelloResponse) ProtoMessage() {} + +func (x *SayHelloResponse) ProtoReflect() protoreflect.Message { + mi := &file_testing_interceptor_v1_greeter_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SayHelloResponse.ProtoReflect.Descriptor instead. +func (*SayHelloResponse) Descriptor() ([]byte, []int) { + return file_testing_interceptor_v1_greeter_proto_rawDescGZIP(), []int{1} +} + +func (x *SayHelloResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_testing_interceptor_v1_greeter_proto protoreflect.FileDescriptor + +var file_testing_interceptor_v1_greeter_proto_rawDesc = []byte{ + 0x0a, 0x24, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, + 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x22, 0x25, + 0x0a, 0x0f, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2c, 0x0a, 0x10, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x32, 0x71, 0x0a, 0x0e, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x12, 0x27, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x79, 0x48, 0x65, + 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x3b, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x6f, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_testing_interceptor_v1_greeter_proto_rawDescOnce sync.Once + file_testing_interceptor_v1_greeter_proto_rawDescData = file_testing_interceptor_v1_greeter_proto_rawDesc +) + +func file_testing_interceptor_v1_greeter_proto_rawDescGZIP() []byte { + file_testing_interceptor_v1_greeter_proto_rawDescOnce.Do(func() { + file_testing_interceptor_v1_greeter_proto_rawDescData = protoimpl.X.CompressGZIP(file_testing_interceptor_v1_greeter_proto_rawDescData) + }) + return file_testing_interceptor_v1_greeter_proto_rawDescData +} + +var file_testing_interceptor_v1_greeter_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_testing_interceptor_v1_greeter_proto_goTypes = []interface{}{ + (*SayHelloRequest)(nil), // 0: testing.interceptor.v1.SayHelloRequest + (*SayHelloResponse)(nil), // 1: testing.interceptor.v1.SayHelloResponse +} +var file_testing_interceptor_v1_greeter_proto_depIdxs = []int32{ + 0, // 0: testing.interceptor.v1.GreeterService.SayHello:input_type -> testing.interceptor.v1.SayHelloRequest + 1, // 1: testing.interceptor.v1.GreeterService.SayHello:output_type -> testing.interceptor.v1.SayHelloResponse + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_testing_interceptor_v1_greeter_proto_init() } +func file_testing_interceptor_v1_greeter_proto_init() { + if File_testing_interceptor_v1_greeter_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_testing_interceptor_v1_greeter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SayHelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_testing_interceptor_v1_greeter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SayHelloResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_testing_interceptor_v1_greeter_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_testing_interceptor_v1_greeter_proto_goTypes, + DependencyIndexes: file_testing_interceptor_v1_greeter_proto_depIdxs, + MessageInfos: file_testing_interceptor_v1_greeter_proto_msgTypes, + }.Build() + File_testing_interceptor_v1_greeter_proto = out.File + file_testing_interceptor_v1_greeter_proto_rawDesc = nil + file_testing_interceptor_v1_greeter_proto_goTypes = nil + file_testing_interceptor_v1_greeter_proto_depIdxs = nil +} diff --git a/internal/gen/testing/interceptor/greeter_grpc.pb.go b/internal/gen/testing/interceptor/greeter_grpc.pb.go new file mode 100644 index 000000000..7a4e8ce89 --- /dev/null +++ b/internal/gen/testing/interceptor/greeter_grpc.pb.go @@ -0,0 +1,103 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package interceptor + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// GreeterServiceClient is the client API for GreeterService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type GreeterServiceClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *SayHelloRequest, opts ...grpc.CallOption) (*SayHelloResponse, error) +} + +type greeterServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterServiceClient(cc grpc.ClientConnInterface) GreeterServiceClient { + return &greeterServiceClient{cc} +} + +func (c *greeterServiceClient) SayHello(ctx context.Context, in *SayHelloRequest, opts ...grpc.CallOption) (*SayHelloResponse, error) { + out := new(SayHelloResponse) + err := c.cc.Invoke(ctx, "/testing.interceptor.v1.GreeterService/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServiceServer is the server API for GreeterService service. +// All implementations must embed UnimplementedGreeterServiceServer +// for forward compatibility +type GreeterServiceServer interface { + // Sends a greeting + SayHello(context.Context, *SayHelloRequest) (*SayHelloResponse, error) + mustEmbedUnimplementedGreeterServiceServer() +} + +// UnimplementedGreeterServiceServer must be embedded to have forward compatible implementations. +type UnimplementedGreeterServiceServer struct { +} + +func (UnimplementedGreeterServiceServer) SayHello(context.Context, *SayHelloRequest) (*SayHelloResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedGreeterServiceServer) mustEmbedUnimplementedGreeterServiceServer() {} + +// UnsafeGreeterServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterServiceServer will +// result in compilation errors. +type UnsafeGreeterServiceServer interface { + mustEmbedUnimplementedGreeterServiceServer() +} + +func RegisterGreeterServiceServer(s grpc.ServiceRegistrar, srv GreeterServiceServer) { + s.RegisterService(&GreeterService_ServiceDesc, srv) +} + +func _GreeterService_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SayHelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServiceServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/testing.interceptor.v1.GreeterService/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServiceServer).SayHello(ctx, req.(*SayHelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// GreeterService_ServiceDesc is the grpc.ServiceDesc for GreeterService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var GreeterService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "testing.interceptor.v1.GreeterService", + HandlerType: (*GreeterServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _GreeterService_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "testing/interceptor/v1/greeter.proto", +} diff --git a/internal/proto/local/controller/api/resources/targets/v1/target.proto b/internal/proto/local/controller/api/resources/targets/v1/target.proto index 82aaf87c5..368995f3f 100644 --- a/internal/proto/local/controller/api/resources/targets/v1/target.proto +++ b/internal/proto/local/controller/api/resources/targets/v1/target.proto @@ -112,13 +112,13 @@ message Target { // The type of the Target. string type = 90; - // The IDs of the Host Sets associated with this Target. + // Output only. The IDs of the Host Sets associated with this Target. repeated string host_set_ids = 100 [json_name="host_set_ids"]; // Output only. The Host Sets associated with this Target. repeated HostSet host_sets = 110 [json_name="host_sets"]; - // The IDs of the Host Sources associated with this Target. + // Output only. The IDs of the Host Sources associated with this Target. repeated string host_source_ids = 420 [json_name="host_source_ids"]; // Output only. The Host Sources associated with this Target. @@ -133,12 +133,12 @@ message Target { // Optional boolean expression to filter the workers that are allowed to satisfy this request. google.protobuf.StringValue worker_filter = 140 [json_name="worker_filter", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this: "worker_filter" that: "WorkerFilter"}]; - // The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. + // Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. repeated string application_credential_library_ids = 150 [json_name="application_credential_library_ids", deprecated = true]; // Output only. The application credential libraries associated with this Target. Deprecated: use application_credential_sources instead. repeated CredentialLibrary application_credential_libraries = 180 [json_name="application_credential_libraries", deprecated = true]; - // The IDs of the application credential source ids associated with this Target. + // Output only. The IDs of the application credential source ids associated with this Target. repeated string application_credential_source_ids = 400 [json_name="application_credential_source_ids"]; // Output only. The application credential sources associated with this Target. repeated CredentialSource application_credential_sources = 410 [json_name="application_credential_sources"]; @@ -238,4 +238,4 @@ message SessionAuthorization { // Output only. The credentials for this session. repeated SessionCredential credentials = 110 [json_name="credentials"]; -} \ No newline at end of file +} diff --git a/internal/proto/local/controller/api/v1/empty_msg.proto b/internal/proto/local/controller/api/v1/empty_msg.proto new file mode 100644 index 000000000..2be840d52 --- /dev/null +++ b/internal/proto/local/controller/api/v1/empty_msg.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package controller.api.v1; + +option go_package = "github.com/hashicorp/boundary/internal/gen/controller/api;api"; + +// EmptyResponse represents an empty response +message EmptyResponse {} diff --git a/internal/proto/local/controller/auth/v1/auth.proto b/internal/proto/local/controller/auth/v1/auth.proto new file mode 100644 index 000000000..840ffa12b --- /dev/null +++ b/internal/proto/local/controller/auth/v1/auth.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package controller.auth.v1; + +option go_package = "github.com/hashicorp/boundary/internal/gen/controller/auth;auth"; + +// RequestInfo contains request parameters necessary for checking authn/authz +message RequestInfo { + // path of the request + string path = 10; + + // method (http verb) of the request + string method = 20; + + // public_id for the request + string public_id = 30; + + // encrypted_token from the request + string encrypted_token = 40; + + // token from the request + string token = 50; + + // token_format of the request's token + uint32 token_format = 60; + + // scope_id_override for the request (helpful for tests) + string scope_id_override = 70; + + // user_id_override for the request (helpful for tests) + string user_id_override = 80; + + // disable_authz_failures for the request (helpful for tests) + bool disable_authz_failures = 90; + + // disable_auth_entirely for the request (helpful for tests) + bool disable_auth_entirely = 100; + + // ticket is a unique id that allows the grpc-gateway to verify that the info + // came from its companion http proxy + string ticket = 110; +} diff --git a/internal/proto/local/controller/servers/v1/servers.proto b/internal/proto/local/controller/servers/v1/servers.proto index 7adf4bbb3..ecb426497 100644 --- a/internal/proto/local/controller/servers/v1/servers.proto +++ b/internal/proto/local/controller/servers/v1/servers.proto @@ -17,6 +17,7 @@ message Server { // private ID and name to be identical, and there is currently no reason we // can think of to allow a given server's name to match another within the // same cluster. So we simply use the private ID and deprecate this. + // @inject_tag: `gorm:"-"` string name = 30 [deprecated = true]; // Description of the resource diff --git a/internal/proto/local/controller/storage/target/store/v1/target.proto b/internal/proto/local/controller/storage/target/store/v1/target.proto index 837b66ae1..748827bb4 100644 --- a/internal/proto/local/controller/storage/target/store/v1/target.proto +++ b/internal/proto/local/controller/storage/target/store/v1/target.proto @@ -73,70 +73,6 @@ message TargetHostSet { timestamp.v1.Timestamp create_time = 30; } -message TcpTarget { - // public_id is used to access the TargetTcp via an API - // @inject_tag: gorm:"primary_key" - string public_id = 10; - - // scope id for the TargetTcp - // @inject_tag: `gorm:"default:null"` - string scope_id = 20; - - // name is the optional friendly name used to - // access the TargetTcp via an API - // @inject_tag: `gorm:"default:null"` - string name = 30 - [(custom_options.v1.mask_mapping) = { this: "name" that: "name" }]; - - // description of the TargetTcp - // @inject_tag: `gorm:"default:null"` - string description = 40 [(custom_options.v1.mask_mapping) = { - this: "description" - that: "description" - }]; - - // create_time from the RDBMS - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp create_time = 50; - - // update_time from the RDBMS - // @inject_tag: `gorm:"default:current_timestamp"` - timestamp.v1.Timestamp update_time = 60; - - // version allows optimistic locking of the TargetTcp when modifying the - // TargetTcp - // @inject_tag: `gorm:"default:null"` - uint32 version = 70; - - // default port of the TargetTcp - // @inject_tag: `gorm:"default:null"` - uint32 default_port = 80 [(custom_options.v1.mask_mapping) = { - this: "DefaultPort" - that: "attributes.default_port" - }]; - - // Maximum total lifetime of a created session, in seconds - // @inject_tag: `gorm:"default:null"` - uint32 session_max_seconds = 100 [(custom_options.v1.mask_mapping) = { - this: "SessionMaxSeconds" - that: "session_max_seconds" - }]; - - // Maximum number of connections in a session - // @inject_tag: `gorm:"default:null"` - int32 session_connection_limit = 110 [(custom_options.v1.mask_mapping) = { - this: "SessionConnectionLimit" - that: "session_connection_limit" - }]; - - // A boolean expression that allows filtering the workers that can handle a session - // @inject_tag: `gorm:"default:null"` - string worker_filter = 120 [(custom_options.v1.mask_mapping) = { - this: "WorkerFilter" - that: "worker_filter" - }]; -} - message CredentialLibrary { // target_id of the Target // @inject_tag: gorm:"primary_key" diff --git a/internal/proto/local/controller/storage/target/tcp/store/v1/target.proto b/internal/proto/local/controller/storage/target/tcp/store/v1/target.proto new file mode 100644 index 000000000..beffda5f0 --- /dev/null +++ b/internal/proto/local/controller/storage/target/tcp/store/v1/target.proto @@ -0,0 +1,72 @@ +syntax = "proto3"; + +package controller.storage.target.tcp.store.v1; +option go_package = "github.com/hashicorp/boundary/internal/target/tcp/store;store"; + +import "controller/storage/timestamp/v1/timestamp.proto"; +import "controller/custom_options/v1/options.proto"; + +message Target { + // public_id is used to access the tcp.Target via an API + // @inject_tag: gorm:"primary_key" + string public_id = 10; + + // scope id for the tcp.Target + // @inject_tag: `gorm:"default:null"` + string scope_id = 20; + + // name is the optional friendly name used to + // access the tcp.Target via an API + // @inject_tag: `gorm:"default:null"` + string name = 30 + [(custom_options.v1.mask_mapping) = { this: "name" that: "name" }]; + + // description of the tcp.Target + // @inject_tag: `gorm:"default:null"` + string description = 40 [(custom_options.v1.mask_mapping) = { + this: "description" + that: "description" + }]; + + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp create_time = 50; + + // update_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + timestamp.v1.Timestamp update_time = 60; + + // version allows optimistic locking of the tcp.Target when modifying the + // tcp.Target + // @inject_tag: `gorm:"default:null"` + uint32 version = 70; + + // default port of the tcp.Target + // @inject_tag: `gorm:"default:null"` + uint32 default_port = 80 [(custom_options.v1.mask_mapping) = { + this: "DefaultPort" + that: "attributes.default_port" + }]; + + // Maximum total lifetime of a created session, in seconds + // @inject_tag: `gorm:"default:null"` + uint32 session_max_seconds = 100 [(custom_options.v1.mask_mapping) = { + this: "SessionMaxSeconds" + that: "session_max_seconds" + }]; + + // Maximum number of connections in a session + // @inject_tag: `gorm:"default:null"` + int32 session_connection_limit = 110 [(custom_options.v1.mask_mapping) = { + this: "SessionConnectionLimit" + that: "session_connection_limit" + }]; + + // A boolean expression that allows filtering the workers that can handle a session + // @inject_tag: `gorm:"default:null"` + string worker_filter = 120 [(custom_options.v1.mask_mapping) = { + this: "WorkerFilter" + that: "worker_filter" + }]; +} + diff --git a/internal/proto/local/errors/v1/errors.proto b/internal/proto/local/errors/v1/errors.proto new file mode 100644 index 000000000..d8acb33fc --- /dev/null +++ b/internal/proto/local/errors/v1/errors.proto @@ -0,0 +1,41 @@ +syntax = "proto3"; + +package errors.v1; + +option go_package = "github.com/hashicorp/boundary/internal/gen/errors;errors"; + +import "controller/api/v1/error.proto"; + +// Err is a protobuf representation of a domain error +message Err { + // code is the error's code, which can be used to get the error's + // errorCodeInfo, which contains the error's Kind and Message + uint32 code = 10; + + // msg for the error + string msg = 20; + + // op represents the operation raising/propagating an error and is optional. + string op = 30; + + // wrapped represents an optionally wrapped error + oneof wrapped { + // err is a wrapped Err (aka a domain error) + Err err = 40; + + // std_error is a wrapped std error + string std_error = 50; + + // no error or err is wrapped + bool none = 60; + } +} + +// ApiError is a protobuf representation of a controller ApiError +message ApiError { + // api_error is the controller ApiError + controller.api.v1.Error api_error = 10; + + // status is the status code assocatiated with the api error + int32 status = 20; +} \ No newline at end of file diff --git a/internal/proto/local/testing/interceptor/v1/greeter.proto b/internal/proto/local/testing/interceptor/v1/greeter.proto new file mode 100644 index 000000000..f1fc1b4e5 --- /dev/null +++ b/internal/proto/local/testing/interceptor/v1/greeter.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package testing.interceptor.v1; + +option go_package = "github.com/hashicorp/boundary/internal/gen/testing/interceptor;interceptor"; + +// The greeting service definition. This is just a dead simple service that can +// be used to test interceptors. (See controller.startTestGreeterService) +service GreeterService { + // Sends a greeting + rpc SayHello(SayHelloRequest) returns (SayHelloResponse) {} +} + +// The request message containing the user's name. +message SayHelloRequest { + string name = 1; +} + +// The response message containing the greetings +message SayHelloResponse { + string message = 1; +} \ No newline at end of file diff --git a/internal/servers/controller/auth/auth.go b/internal/servers/controller/auth/auth.go index 6fd9f6946..b72d4b7c5 100644 --- a/internal/servers/controller/auth/auth.go +++ b/internal/servers/controller/auth/auth.go @@ -11,7 +11,9 @@ import ( "github.com/hashicorp/boundary/api/recovery" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/errors" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/gen/controller/tokens" + "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/perms" @@ -27,7 +29,7 @@ import ( "google.golang.org/protobuf/proto" ) -type TokenFormat int +type TokenFormat uint32 const ( // We weren't given one or couldn't parse it @@ -51,22 +53,6 @@ type key int var verifierKey key -// RequestInfo contains request parameters necessary for checking authn/authz -type RequestInfo struct { - Path string - Method string - PublicId string - EncryptedToken string - Token string - TokenFormat TokenFormat - - // The following are useful for tests - scopeIdOverride string - userIdOverride string - DisableAuthzFailures bool - DisableAuthEntirely bool -} - type VerifyResults struct { UserId string AuthTokenId string @@ -108,7 +94,7 @@ type verifier struct { authTokenRepoFn common.AuthTokenRepoFactory serversRepoFn common.ServersRepoFactory kms *kms.Kms - requestInfo RequestInfo + requestInfo *authpb.RequestInfo res *perms.Resource act action.Type ctx context.Context @@ -124,7 +110,7 @@ func NewVerifierContext(ctx context.Context, authTokenRepoFn common.AuthTokenRepoFactory, serversRepoFn common.ServersRepoFactory, kms *kms.Kms, - requestInfo RequestInfo) context.Context { + requestInfo *authpb.RequestInfo) context.Context { return context.WithValue(ctx, verifierKey, &verifier{ iamRepoFn: iamRepoFn, authTokenRepoFn: authTokenRepoFn, @@ -174,7 +160,7 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { // without fuss if v.requestInfo.DisableAuthEntirely { const op = "auth.(disabled).lookupScope" - ret.Scope.Id = v.requestInfo.scopeIdOverride + ret.Scope.Id = v.requestInfo.ScopeIdOverride if ret.Scope.Id == "" { ret.Scope.Id = opts.withScopeId } @@ -214,7 +200,7 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { ParentScopeId: scp.GetParentId(), } } - ret.UserId = v.requestInfo.userIdOverride + ret.UserId = v.requestInfo.UserIdOverride if reqInfo != nil { reqInfo.UserId = ret.UserId } @@ -291,40 +277,40 @@ func Verify(ctx context.Context, opt ...Option) (ret VerifyResults) { func (v *verifier) decryptToken(ctx context.Context) { const op = "auth.(verifier).decryptToken" switch v.requestInfo.TokenFormat { - case AuthTokenTypeUnknown: + case uint32(AuthTokenTypeUnknown): // Nothing to decrypt return - case AuthTokenTypeBearer, AuthTokenTypeSplitCookie: + case uint32(AuthTokenTypeBearer), uint32(AuthTokenTypeSplitCookie): if v.kms == nil { event.WriteError(ctx, op, stderrors.New("no KMS object available to authz system")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } tokenRepo, err := v.authTokenRepoFn() if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("failed to get authtoken repo")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } at, err := tokenRepo.LookupAuthToken(v.ctx, v.requestInfo.PublicId) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("failed to look up auth token by public ID")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } if at == nil { event.WriteError(ctx, op, stderrors.New("nil result from looking up auth token by public ID")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } tokenWrapper, err := v.kms.GetWrapper(v.ctx, at.GetScopeId(), kms.KeyPurposeTokens) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("unable to get wrapper for tokens; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } @@ -333,62 +319,62 @@ func (v *verifier) decryptToken(ctx context.Context) { case globals.ServiceTokenV1: default: event.WriteError(ctx, op, stderrors.New("unknown token encryption version; continuing as anonymous user"), event.WithInfo("version", version)) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } marshaledToken, err := base58.FastBase58Decoding(v.requestInfo.EncryptedToken[len(globals.ServiceTokenV1):]) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error unmarshaling base58 token; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } blobInfo := new(wrapping.EncryptedBlobInfo) if err := proto.Unmarshal(marshaledToken, blobInfo); err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error decoding encrypted token; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } s1Bytes, err := tokenWrapper.Decrypt(v.ctx, blobInfo, []byte(v.requestInfo.PublicId)) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error decrypting encrypted token; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } var s1Info tokens.S1TokenInfo if err := proto.Unmarshal(s1Bytes, &s1Info); err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("error unmarshaling token info; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } - if v.requestInfo.TokenFormat == AuthTokenTypeUnknown || s1Info.Token == "" || v.requestInfo.PublicId == "" { + if v.requestInfo.TokenFormat == uint32(AuthTokenTypeUnknown) || s1Info.Token == "" || v.requestInfo.PublicId == "" { event.WriteError(ctx, op, stderrors.New("after parsing, could not find valid token; continuing as anonymous user")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } v.requestInfo.Token = s1Info.Token return - case AuthTokenTypeRecoveryKms: + case uint32(AuthTokenTypeRecoveryKms): if v.kms == nil { event.WriteError(ctx, op, stderrors.New("no KMS object available to authz system")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } wrapper := v.kms.GetExternalWrappers().Recovery() if wrapper == nil { event.WriteError(ctx, op, stderrors.New("no recovery KMS is available")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } info, err := recovery.ParseRecoveryToken(v.ctx, wrapper, v.requestInfo.EncryptedToken) if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("decrypt recovery token: error parsing and validating recovery token")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } // If we add the validity period to the creation time (which we've @@ -396,18 +382,18 @@ func (v *verifier) decryptToken(ctx context.Context) { // it's before now, it's expired and might be a replay. if info.CreationTime.Add(globals.RecoveryTokenValidityPeriod).Before(time.Now()) { event.WriteError(ctx, op, stderrors.New("recovery token has expired (possible replay attack)")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } repo, err := v.serversRepoFn() if err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("decrypt recovery token: error fetching servers repo")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } if err := repo.AddRecoveryNonce(v.ctx, info.Nonce); err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("decrypt recovery token: error adding nonce to database (possible replay attack)")) - v.requestInfo.TokenFormat = AuthTokenTypeUnknown + v.requestInfo.TokenFormat = uint32(AuthTokenTypeUnknown) return } event.WriteError(ctx, op, stderrors.New("recovery KMS was used to authorize a call"), event.WithInfo("url", v.requestInfo.Path, "method", v.requestInfo.Method)) @@ -426,15 +412,15 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes // Validate the token and fetch the corresponding user ID switch v.requestInfo.TokenFormat { - case AuthTokenTypeUnknown: + case uint32(AuthTokenTypeUnknown): // Nothing; remain as the anonymous user - case AuthTokenTypeRecoveryKms: + case uint32(AuthTokenTypeRecoveryKms): // We validated the encrypted token in decryptToken and handled the // nonces there, so just set the user userId = "u_recovery" - case AuthTokenTypeBearer, AuthTokenTypeSplitCookie: + case uint32(AuthTokenTypeBearer), uint32(AuthTokenTypeSplitCookie): if v.requestInfo.Token == "" { // This will end up staying as the anonymous user break @@ -500,7 +486,7 @@ func (v verifier) performAuthCheck(ctx context.Context) (aclResults perms.ACLRes } // At this point we don't need to look up grants since it's automatically allowed - if v.requestInfo.TokenFormat == AuthTokenTypeRecoveryKms { + if v.requestInfo.TokenFormat == uint32(AuthTokenTypeRecoveryKms) { aclResults.AuthenticationFinished = true aclResults.Authorized = true retErr = nil @@ -561,7 +547,7 @@ func (r *VerifyResults) FetchActionSetForType(ctx context.Context, typ resource. func (r *VerifyResults) fetchActions(id string, typ resource.Type, availableActions action.ActionSet, opt ...Option) action.ActionSet { switch { case r.v.requestInfo.DisableAuthEntirely, - r.v.requestInfo.TokenFormat == AuthTokenTypeRecoveryKms: + r.v.requestInfo.TokenFormat == uint32(AuthTokenTypeRecoveryKms): // TODO: See if we can be better about what we return with the anonymous // user and recovery KMS, perhaps given exclusionary options for each return availableActions @@ -598,7 +584,7 @@ func (r *VerifyResults) fetchActions(id string, typ resource.Type, availableActi func (r *VerifyResults) FetchOutputFields(res perms.Resource, act action.Type) perms.OutputFieldsMap { switch { - case r.v.requestInfo.TokenFormat == AuthTokenTypeRecoveryKms: + case r.v.requestInfo.TokenFormat == uint32(AuthTokenTypeRecoveryKms): return perms.OutputFieldsMap{"*": true} case r.v.requestInfo.DisableAuthEntirely: return nil @@ -611,7 +597,7 @@ func (r *VerifyResults) FetchOutputFields(res perms.Resource, act action.Type) p // split cookies and parses it. If it cannot be parsed successfully, the issue // is logged and we return blank, so logic will continue as the anonymous user. // The public ID and _encrypted_ token are returned along with the token format. -func GetTokenFromRequest(ctx context.Context, kmsCache *kms.Kms, req *http.Request) (string, string, TokenFormat) { +func GetTokenFromRequest(ctx context.Context, kmsCache *kms.Kms, req *http.Request) (string, string, uint32) { const op = "auth.GetTokenFromRequest" // First, get the token, either from the authorization header or from split // cookies @@ -643,21 +629,21 @@ func GetTokenFromRequest(ctx context.Context, kmsCache *kms.Kms, req *http.Reque // We didn't find auth info or a client screwed up and put in a blank // header instead of nothing at all, so return blank which will indicate // the anonymouse user - return "", "", AuthTokenTypeUnknown + return "", "", uint32(AuthTokenTypeUnknown) } if strings.HasPrefix(fullToken, "r_") { - return "", fullToken, AuthTokenTypeRecoveryKms + return "", fullToken, uint32(AuthTokenTypeRecoveryKms) } splitFullToken := strings.Split(fullToken, "_") if len(splitFullToken) != 3 { event.WriteError(ctx, op, stderrors.New("unexpected number of segments in token; continuing as anonymous user"), event.WithInfo("expected", 3, "found", len(splitFullToken))) - return "", "", AuthTokenTypeUnknown + return "", "", uint32(AuthTokenTypeUnknown) } publicId := strings.Join(splitFullToken[0:2], "_") encryptedToken := splitFullToken[2] - return publicId, encryptedToken, receivedTokenType + return publicId, encryptedToken, uint32(receivedTokenType) } diff --git a/internal/servers/controller/auth/auth_test.go b/internal/servers/controller/auth/auth_test.go index 574ac0cb3..e90b523f2 100644 --- a/internal/servers/controller/auth/auth_test.go +++ b/internal/servers/controller/auth/auth_test.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" @@ -107,17 +108,17 @@ func TestAuthTokenAuthenticator(t *testing.T) { } // Add values for authn/authz checking - requestInfo := RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: req.URL.Path, Method: req.Method, } requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = GetTokenFromRequest(context.TODO(), kms, req) - assert.Equal(t, tc.tokenFormat, requestInfo.TokenFormat) + assert.Equal(t, uint32(tc.tokenFormat), requestInfo.TokenFormat) if tc.userId == "" { return } - ctx := NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo) + ctx := NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo) v, ok := ctx.Value(verifierKey).(*verifier) require.True(t, ok) diff --git a/internal/servers/controller/auth/testing.go b/internal/servers/controller/auth/testing.go index 731da8bd2..6a50b5b49 100644 --- a/internal/servers/controller/auth/testing.go +++ b/internal/servers/controller/auth/testing.go @@ -3,6 +3,7 @@ package auth import ( "context" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/controller/common" ) @@ -11,16 +12,16 @@ import ( // auth checking entirely disabled. Supported options: WithScopeId an WithUserId // are used directly; WithKms is passed through into the verifier context. func DisabledAuthTestContext(iamRepoFn common.IamRepoFactory, scopeId string, opt ...Option) context.Context { - reqInfo := RequestInfo{DisableAuthEntirely: true} + reqInfo := authpb.RequestInfo{DisableAuthEntirely: true} opts := getOpts(opt...) - reqInfo.scopeIdOverride = opts.withScopeId - if reqInfo.scopeIdOverride == "" { - reqInfo.scopeIdOverride = scopeId + reqInfo.ScopeIdOverride = opts.withScopeId + if reqInfo.ScopeIdOverride == "" { + reqInfo.ScopeIdOverride = scopeId } - reqInfo.userIdOverride = opts.withUserId - if reqInfo.userIdOverride == "" { - reqInfo.userIdOverride = "u_auth" + reqInfo.UserIdOverride = opts.withUserId + if reqInfo.UserIdOverride == "" { + reqInfo.UserIdOverride = "u_auth" } requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{}) - return NewVerifierContext(requestContext, iamRepoFn, nil, nil, opts.withKms, reqInfo) + return NewVerifierContext(requestContext, iamRepoFn, nil, nil, opts.withKms, &reqInfo) } diff --git a/internal/servers/controller/common/scopeids/scope_ids_test.go b/internal/servers/controller/common/scopeids/scope_ids_test.go index 570098e9d..d0598b975 100644 --- a/internal/servers/controller/common/scopeids/scope_ids_test.go +++ b/internal/servers/controller/common/scopeids/scope_ids_test.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" @@ -229,9 +230,9 @@ func TestListingScopeIds(t *testing.T) { authTokenRepoFn, serversRepoFn, kms, - auth.RequestInfo{ + &authpb.RequestInfo{ Token: at.GetToken(), - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: at.GetPublicId(), }) diff --git a/internal/servers/controller/controller.go b/internal/servers/controller/controller.go index 9aa83c540..7e6175878 100644 --- a/internal/servers/controller/controller.go +++ b/internal/servers/controller/controller.go @@ -6,6 +6,8 @@ import ( "fmt" "sync" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" @@ -32,6 +34,7 @@ import ( "github.com/hashicorp/go-secure-stdlib/mlock" "github.com/patrickmn/go-cache" ua "go.uber.org/atomic" + "google.golang.org/grpc" ) type Controller struct { @@ -50,6 +53,12 @@ type Controller struct { // Used for testing and tracking worker health workerStatusUpdateTimes *sync.Map + // grpc gateway server + gatewayServer *grpc.Server + gatewayTicket string + gatewayListener gatewayListener + gatewayMux *runtime.ServeMux + // Repo factory methods AuthTokenRepoFn common.AuthTokenRepoFactory VaultCredentialRepoFn common.VaultCredentialRepoFactory @@ -218,7 +227,7 @@ func (c *Controller) Start() error { if err := c.scheduler.Start(c.baseContext, &c.schedulerWg); err != nil { return fmt.Errorf("error starting scheduler: %w", err) } - if err := c.startListeners(); err != nil { + if err := c.startListeners(c.baseContext); err != nil { return fmt.Errorf("error starting controller listeners: %w", err) } diff --git a/internal/servers/controller/gateway.go b/internal/servers/controller/gateway.go new file mode 100644 index 000000000..41f6d76a9 --- /dev/null +++ b/internal/servers/controller/gateway.go @@ -0,0 +1,67 @@ +package controller + +import ( + "context" + "math" + "net" + + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/hashicorp/boundary/globals" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers/controller/common" + "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "google.golang.org/grpc" + "google.golang.org/grpc/test/bufconn" +) + +// newGatewayListener will create an in-memory listener +func newGatewayListener() (gatewayListener, string) { + buffer := globals.DefaultMaxRequestSize // seems like a reasonable size for the ring buffer, but then happily change the size if more info becomes available + return bufconn.Listen(int(buffer)), "" +} + +const gatewayTarget = "" + +type gatewayListener interface { + net.Listener + Dial() (net.Conn, error) +} + +func gatewayDialOptions(lis gatewayListener) []grpc.DialOption { + return []grpc.DialOption{ + grpc.WithInsecure(), + grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return lis.Dial() + }), + } +} + +func newGatewayMux() *runtime.ServeMux { + return runtime.NewServeMux( + runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{ + Marshaler: handlers.JSONMarshaler(), + }), + runtime.WithErrorHandler(handlers.ErrorHandler()), + runtime.WithForwardResponseOption(handlers.OutgoingResponseFilter), + ) +} + +func newGatewayServer(ctx context.Context, iamRepoFn common.IamRepoFactory, authTokenRepoFn common.AuthTokenRepoFactory, serversRepoFn common.ServersRepoFactory, kms *kms.Kms) (*grpc.Server, string, error) { + const op = "controller.newGatewayServer" + ticket, err := db.NewPrivateId("gwticket") + if err != nil { + return nil, "", errors.Wrap(ctx, err, op, errors.WithMsg("unable to generate gateway ticket")) + } + requestCtxInterceptor, err := requestCtxInterceptor(ctx, iamRepoFn, authTokenRepoFn, serversRepoFn, kms, ticket) + if err != nil { + return nil, "", err + } + return grpc.NewServer( + grpc.MaxRecvMsgSize(math.MaxInt32), + grpc.MaxSendMsgSize(math.MaxInt32), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(requestCtxInterceptor, errorInterceptor(ctx), statusCodeInterceptor(ctx))), + ), ticket, nil +} diff --git a/internal/servers/controller/handler.go b/internal/servers/controller/handler.go index d59a39c82..43b9b72a2 100644 --- a/internal/servers/controller/handler.go +++ b/internal/servers/controller/handler.go @@ -13,35 +13,36 @@ import ( "strings" "time" - "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/gen/controller/api/services" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/observability/event" - "github.com/hashicorp/boundary/internal/requests" "github.com/hashicorp/boundary/internal/servers/common" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/handlers/accounts" "github.com/hashicorp/boundary/internal/servers/controller/handlers/authmethods" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens" "github.com/hashicorp/boundary/internal/servers/controller/handlers/credentiallibraries" "github.com/hashicorp/boundary/internal/servers/controller/handlers/credentialstores" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/groups" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_sets" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts" "github.com/hashicorp/boundary/internal/servers/controller/handlers/managed_groups" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/roles" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/scopes" "github.com/hashicorp/boundary/internal/servers/controller/handlers/sessions" "github.com/hashicorp/boundary/internal/servers/controller/handlers/targets" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/users" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-secure-stdlib/listenerutil" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/mr-tron/base58" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/proto" "github.com/hashicorp/boundary/internal/servers/controller/handlers" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/groups" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/host_catalogs" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/hosts" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/roles" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/scopes" - "github.com/hashicorp/boundary/internal/servers/controller/handlers/users" ) type HandlerProperties struct { @@ -78,129 +79,168 @@ func handleGrpcGateway(c *Controller, props HandlerProperties) (http.Handler, er // Register*ServiceHandlerServer methods ignore the passed in ctx. Using it // now however in case this changes in the future. ctx := props.CancelCtx - mux := runtime.NewServeMux( - runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.HTTPBodyMarshaler{ - Marshaler: handlers.JSONMarshaler(), - }), - runtime.WithErrorHandler(handlers.ErrorHandler()), - runtime.WithForwardResponseOption(handlers.OutgoingInterceptor), - ) - hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn, c.HostPluginRepoFn, c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create host catalog handler service: %w", err) + currentServices := c.gatewayServer.GetServiceInfo() + dialOptions := gatewayDialOptions(c.gatewayListener) + + if _, ok := currentServices[services.HostCatalogService_ServiceDesc.ServiceName]; !ok { + hcs, err := host_catalogs.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn, c.HostPluginRepoFn, c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create host catalog handler service: %w", err) + } + services.RegisterHostCatalogServiceServer(c.gatewayServer, hcs) + if err := services.RegisterHostCatalogServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register host catalog service handler: %w", err) + } } - if err := services.RegisterHostCatalogServiceHandlerServer(ctx, mux, hcs); err != nil { - return nil, fmt.Errorf("failed to register host catalog service handler: %w", err) + if _, ok := currentServices[services.HostSetService_ServiceDesc.ServiceName]; !ok { + hss, err := host_sets.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create host set handler service: %w", err) + } + services.RegisterHostSetServiceServer(c.gatewayServer, hss) + if err := services.RegisterHostSetServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register host set service handler: %w", err) + } } - hss, err := host_sets.NewService(c.StaticHostRepoFn, c.PluginHostRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create host set handler service: %w", err) + if _, ok := currentServices[services.HostService_ServiceDesc.ServiceName]; !ok { + hs, err := hosts.NewService(c.StaticHostRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create host handler service: %w", err) + } + services.RegisterHostServiceServer(c.gatewayServer, hs) + if err := services.RegisterHostServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register host service handler: %w", err) + } } - if err := services.RegisterHostSetServiceHandlerServer(ctx, mux, hss); err != nil { - return nil, fmt.Errorf("failed to register host set service handler: %w", err) + if _, ok := currentServices[services.AccountService_ServiceDesc.ServiceName]; !ok { + accts, err := accounts.NewService(c.PasswordAuthRepoFn, c.OidcRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create account handler service: %w", err) + } + services.RegisterAccountServiceServer(c.gatewayServer, accts) + if err := services.RegisterAccountServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register account service handler: %w", err) + } } - hs, err := hosts.NewService(c.StaticHostRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create host handler service: %w", err) + if _, ok := currentServices[services.AuthMethodService_ServiceDesc.ServiceName]; !ok { + authMethods, err := authmethods.NewService(c.kms, c.PasswordAuthRepoFn, c.OidcRepoFn, c.IamRepoFn, c.AuthTokenRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create auth method handler service: %w", err) + } + services.RegisterAuthMethodServiceServer(c.gatewayServer, authMethods) + if err := services.RegisterAuthMethodServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register auth method service handler: %w", err) + } } - if err := services.RegisterHostServiceHandlerServer(ctx, mux, hs); err != nil { - return nil, fmt.Errorf("failed to register host service handler: %w", err) + if _, ok := currentServices[services.AuthTokenService_ServiceDesc.ServiceName]; !ok { + authtoks, err := authtokens.NewService(c.AuthTokenRepoFn, c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create auth token handler service: %w", err) + } + services.RegisterAuthTokenServiceServer(c.gatewayServer, authtoks) + if err := services.RegisterAuthTokenServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register auth token service handler: %w", err) + } } - accts, err := accounts.NewService(c.PasswordAuthRepoFn, c.OidcRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create account handler service: %w", err) + if _, ok := currentServices[services.ScopeService_ServiceDesc.ServiceName]; !ok { + os, err := scopes.NewService(c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create scope handler service: %w", err) + } + services.RegisterScopeServiceServer(c.gatewayServer, os) + if err := services.RegisterScopeServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register scope service handler: %w", err) + } } - if err := services.RegisterAccountServiceHandlerServer(ctx, mux, accts); err != nil { - return nil, fmt.Errorf("failed to register account service handler: %w", err) + if _, ok := currentServices[services.UserService_ServiceDesc.ServiceName]; !ok { + us, err := users.NewService(c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create user handler service: %w", err) + } + services.RegisterUserServiceServer(c.gatewayServer, us) + if err := services.RegisterUserServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register user service handler: %w", err) + } } - authMethods, err := authmethods.NewService(c.kms, c.PasswordAuthRepoFn, c.OidcRepoFn, c.IamRepoFn, c.AuthTokenRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create auth method handler service: %w", err) + if _, ok := currentServices[services.TargetService_ServiceDesc.ServiceName]; !ok { + ts, err := targets.NewService( + c.kms, + c.TargetRepoFn, + c.IamRepoFn, + c.ServersRepoFn, + c.SessionRepoFn, + c.StaticHostRepoFn, + c.VaultCredentialRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create target handler service: %w", err) + } + services.RegisterTargetServiceServer(c.gatewayServer, ts) + if err := services.RegisterTargetServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register target service handler: %w", err) + } } - if err := services.RegisterAuthMethodServiceHandlerServer(ctx, mux, authMethods); err != nil { - return nil, fmt.Errorf("failed to register auth method service handler: %w", err) + if _, ok := currentServices[services.GroupService_ServiceDesc.ServiceName]; !ok { + gs, err := groups.NewService(c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create group handler service: %w", err) + } + services.RegisterGroupServiceServer(c.gatewayServer, gs) + if err := services.RegisterGroupServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register group service handler: %w", err) + } } - authtoks, err := authtokens.NewService(c.AuthTokenRepoFn, c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create auth token handler service: %w", err) + if _, ok := currentServices[services.RoleService_ServiceDesc.ServiceName]; !ok { + rs, err := roles.NewService(c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create role handler service: %w", err) + } + services.RegisterRoleServiceServer(c.gatewayServer, rs) + if err := services.RegisterRoleServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register role service handler: %w", err) + } } - if err := services.RegisterAuthTokenServiceHandlerServer(ctx, mux, authtoks); err != nil { - return nil, fmt.Errorf("failed to register auth token service handler: %w", err) + if _, ok := currentServices[services.SessionService_ServiceDesc.ServiceName]; !ok { + ss, err := sessions.NewService(c.SessionRepoFn, c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create session handler service: %w", err) + } + services.RegisterSessionServiceServer(c.gatewayServer, ss) + if err := services.RegisterSessionServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register session service handler: %w", err) + } } - os, err := scopes.NewService(c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create scope handler service: %w", err) + if _, ok := currentServices[services.ManagedGroupService_ServiceDesc.ServiceName]; !ok { + mgs, err := managed_groups.NewService(c.OidcRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create managed groups handler service: %w", err) + } + services.RegisterManagedGroupServiceServer(c.gatewayServer, mgs) + if err := services.RegisterManagedGroupServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register managed groups service handler: %w", err) + } } - if err := services.RegisterScopeServiceHandlerServer(ctx, mux, os); err != nil { - return nil, fmt.Errorf("failed to register scope service handler: %w", err) + if _, ok := currentServices[services.CredentialStoreService_ServiceDesc.ServiceName]; !ok { + cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create credential store handler service: %w", err) + } + services.RegisterCredentialStoreServiceServer(c.gatewayServer, cs) + if err := services.RegisterCredentialStoreServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register credential store service handler: %w", err) + } } - us, err := users.NewService(c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create user handler service: %w", err) - } - if err := services.RegisterUserServiceHandlerServer(ctx, mux, us); err != nil { - return nil, fmt.Errorf("failed to register user service handler: %w", err) - } - ts, err := targets.NewService( - ctx, - c.kms, - c.TargetRepoFn, - c.IamRepoFn, - c.ServersRepoFn, - c.SessionRepoFn, - c.PluginHostRepoFn, - c.StaticHostRepoFn, - c.VaultCredentialRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create target handler service: %w", err) - } - if err := services.RegisterTargetServiceHandlerServer(ctx, mux, ts); err != nil { - return nil, fmt.Errorf("failed to register target service handler: %w", err) - } - gs, err := groups.NewService(c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create group handler service: %w", err) - } - if err := services.RegisterGroupServiceHandlerServer(ctx, mux, gs); err != nil { - return nil, fmt.Errorf("failed to register group service handler: %w", err) - } - rs, err := roles.NewService(c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create role handler service: %w", err) - } - if err := services.RegisterRoleServiceHandlerServer(ctx, mux, rs); err != nil { - return nil, fmt.Errorf("failed to register role service handler: %w", err) - } - ss, err := sessions.NewService(c.SessionRepoFn, c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create session handler service: %w", err) - } - if err := services.RegisterSessionServiceHandlerServer(ctx, mux, ss); err != nil { - return nil, fmt.Errorf("failed to register session service handler: %w", err) - } - mgs, err := managed_groups.NewService(c.OidcRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create managed groups handler service: %w", err) - } - if err := services.RegisterManagedGroupServiceHandlerServer(ctx, mux, mgs); err != nil { - return nil, fmt.Errorf("failed to register managed groups service handler: %w", err) - } - cs, err := credentialstores.NewService(c.VaultCredentialRepoFn, c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create credential store handler service: %w", err) - } - if err := services.RegisterCredentialStoreServiceHandlerServer(ctx, mux, cs); err != nil { - return nil, fmt.Errorf("failed to register credential store service handler: %w", err) - } - cl, err := credentiallibraries.NewService(c.VaultCredentialRepoFn, c.IamRepoFn) - if err != nil { - return nil, fmt.Errorf("failed to create credential library handler service: %w", err) - } - if err := services.RegisterCredentialLibraryServiceHandlerServer(ctx, mux, cl); err != nil { - return nil, fmt.Errorf("failed to register credential library service handler: %w", err) + if _, ok := currentServices[services.CredentialLibraryService_ServiceDesc.ServiceName]; !ok { + cl, err := credentiallibraries.NewService(c.VaultCredentialRepoFn, c.IamRepoFn) + if err != nil { + return nil, fmt.Errorf("failed to create credential library handler service: %w", err) + } + services.RegisterCredentialLibraryServiceServer(c.gatewayServer, cl) + if err := services.RegisterCredentialLibraryServiceHandlerFromEndpoint(ctx, c.gatewayMux, gatewayTarget, dialOptions); err != nil { + return nil, fmt.Errorf("failed to register credential library service handler: %w", err) + } } - return mux, nil + return c.gatewayMux, nil } func wrapHandlerWithCommonFuncs(h http.Handler, c *Controller, props HandlerProperties) http.Handler { @@ -238,29 +278,27 @@ func wrapHandlerWithCommonFuncs(h http.Handler, c *Controller, props HandlerProp } // Add values for authn/authz checking - requestInfo := auth.RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: r.URL.Path, Method: r.Method, DisableAuthzFailures: disableAuthzFailures, } requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = auth.GetTokenFromRequest(ctx, c.kms, r) - ctx = auth.NewVerifierContext(ctx, c.IamRepoFn, c.AuthTokenRepoFn, c.ServersRepoFn, c.kms, requestInfo) - // Add general request information to the context. The information from - // the auth verifier context is pretty specifically curated to - // authentication/authorization verification so this is more - // general-purpose. - // - // We could use requests.NewRequestContext but this saves an immediate - // lookup. - ctx = context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{ - Path: r.URL.Path, - Method: r.Method, - }) - - // Set the context back on the request - r = r.WithContext(ctx) + // Serialize the request info to send it across the wire to the + // grpc-gateway via an http header + requestInfo.Ticket = c.gatewayTicket // allows the grpc-gateway to verify the request info came from it's in-memory companion http proxy + marshalledRequestInfo, err := proto.Marshal(&requestInfo) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("error marshaling request info")) + w.WriteHeader(http.StatusInternalServerError) + return + } + // Use the default grpc-gateway mapping rule to pass the request info as + // metadata. + // See: https://pkg.go.dev/github.com/grpc-ecosystem/grpc-gateway/runtime#DefaultHeaderMatcher + r.Header.Set("Grpc-Metadata-"+requestInfoMdKey, base58.FastBase58Encoding(marshalledRequestInfo)) h.ServeHTTP(w, r) }) diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service.go b/internal/servers/controller/handlers/authmethods/authmethod_service.go index 4fb5f0aa2..4bf43902a 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service.go @@ -389,7 +389,7 @@ func (s Service) DeleteAuthMethod(ctx context.Context, req *pbs.DeleteAuthMethod if err != nil { return nil, err } - return nil, nil + return &pbs.DeleteAuthMethodResponse{}, nil } // Authenticate implements the interface pbs.AuthenticationServiceServer. diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go index f1d472a11..8dbd20d93 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go @@ -428,12 +428,14 @@ func TestDelete(t *testing.T) { req: &pbs.DeleteAuthMethodRequest{ Id: pwam.GetPublicId(), }, + res: &pbs.DeleteAuthMethodResponse{}, }, { name: "Delete an Existing OIDC AuthMethod", req: &pbs.DeleteAuthMethodRequest{ Id: oidcam.GetPublicId(), }, + res: &pbs.DeleteAuthMethodResponse{}, }, { name: "Delete bad auth_method id", diff --git a/internal/servers/controller/handlers/authmethods/oidc_test.go b/internal/servers/controller/handlers/authmethods/oidc_test.go index d843c4603..ddc308af8 100644 --- a/internal/servers/controller/handlers/authmethods/oidc_test.go +++ b/internal/servers/controller/handlers/authmethods/oidc_test.go @@ -13,6 +13,8 @@ import ( "time" "github.com/google/go-cmp/cmp" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" + "github.com/hashicorp/boundary/internal/auth/oidc" "github.com/hashicorp/boundary/internal/auth/password" "github.com/hashicorp/boundary/internal/authtoken" @@ -206,9 +208,9 @@ func TestList_FilterNonPublic(t *testing.T) { atRepoFn, serversRepoFn, kmsCache, - auth.RequestInfo{ + &authpb.RequestInfo{ Token: at.GetToken(), - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: at.GetPublicId(), }) }(), diff --git a/internal/servers/controller/handlers/authtokens/authtoken_service_test.go b/internal/servers/controller/handlers/authtokens/authtoken_service_test.go index 784ac8a3e..cff486682 100644 --- a/internal/servers/controller/handlers/authtokens/authtoken_service_test.go +++ b/internal/servers/controller/handlers/authtokens/authtoken_service_test.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/boundary/internal/authtoken" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/requests" @@ -87,15 +88,15 @@ func TestGetSelf(t *testing.T) { require, assert := require.New(t), assert.New(t) // Setup the auth request information req := httptest.NewRequest("GET", fmt.Sprintf("http://127.0.0.1/v1/auth-tokens/%s", tc.readId), nil) - requestInfo := auth.RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: req.URL.Path, Method: req.Method, - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: tc.token.GetPublicId(), Token: tc.token.GetToken(), } - ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo) + ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo) ctx = context.WithValue(ctx, requests.ContextRequestInformationKey, &requests.RequestContext{}) got, err := a.GetAuthToken(ctx, &pbs.GetAuthTokenRequest{Id: tc.readId}) if tc.err != nil { @@ -236,15 +237,15 @@ func TestList_Self(t *testing.T) { require, assert := require.New(t), assert.New(t) // Setup the auth request information req := httptest.NewRequest("GET", fmt.Sprintf("http://127.0.0.1/v1/auth-tokens?scope_id=%s", o.GetPublicId()), nil) - requestInfo := auth.RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: req.URL.Path, Method: req.Method, - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: tc.requester.GetPublicId(), Token: tc.requester.GetToken(), } - ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo) + ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo) got, err := a.ListAuthTokens(ctx, &pbs.ListAuthTokensRequest{ScopeId: o.GetPublicId()}) require.NoError(err) require.Len(got.Items, 1) @@ -482,15 +483,15 @@ func TestDeleteSelf(t *testing.T) { require := require.New(t) // Setup the auth request information req := httptest.NewRequest("DELETE", fmt.Sprintf("http://127.0.0.1/v1/auth-tokens/%s", tc.deleteId), nil) - requestInfo := auth.RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: req.URL.Path, Method: req.Method, - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: tc.token.GetPublicId(), Token: tc.token.GetToken(), } - ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo) + ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo) got, err := a.DeleteAuthToken(ctx, &pbs.DeleteAuthTokenRequest{Id: tc.deleteId}) if tc.err != nil { require.EqualError(err, tc.err.Error()) diff --git a/internal/servers/controller/handlers/credentiallibraries/credentiallibrary_service_test.go b/internal/servers/controller/handlers/credentiallibraries/credentiallibrary_service_test.go index d9dd0118b..681294a1a 100644 --- a/internal/servers/controller/handlers/credentiallibraries/credentiallibrary_service_test.go +++ b/internal/servers/controller/handlers/credentiallibraries/credentiallibrary_service_test.go @@ -555,6 +555,7 @@ func TestDelete(t *testing.T) { name string id string err error + res *pbs.DeleteCredentialLibraryResponse }{ { name: "success", @@ -574,7 +575,7 @@ func TestDelete(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, gErr := s.DeleteCredentialLibrary(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialLibraryRequest{Id: tc.id}) - assert.Nil(t, got) + assert.EqualValuesf(t, tc.res, got, "DeleteCredentialLibrary got response %q, wanted %q", got, tc.res) if tc.err != nil { require.Error(t, gErr) assert.True(t, errors.Is(gErr, tc.err)) diff --git a/internal/servers/controller/handlers/credentialstores/credentialstore_service_test.go b/internal/servers/controller/handlers/credentialstores/credentialstore_service_test.go index 9e087bd46..bfa43c54b 100644 --- a/internal/servers/controller/handlers/credentialstores/credentialstore_service_test.go +++ b/internal/servers/controller/handlers/credentialstores/credentialstore_service_test.go @@ -674,6 +674,7 @@ func TestDelete(t *testing.T) { name string id string err error + res *pbs.DeleteCredentialStoreResponse }{ { name: "success", @@ -693,7 +694,7 @@ func TestDelete(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, gErr := s.DeleteCredentialStore(auth.DisabledAuthTestContext(iamRepoFn, prj.GetPublicId()), &pbs.DeleteCredentialStoreRequest{Id: tc.id}) - assert.Nil(t, got) + 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)) diff --git a/internal/servers/controller/handlers/errors.go b/internal/servers/controller/handlers/errors.go index 1628de364..c23e59164 100644 --- a/internal/servers/controller/handlers/errors.go +++ b/internal/servers/controller/handlers/errors.go @@ -5,31 +5,46 @@ import ( "fmt" "io" "net/http" + "regexp" "sort" + "strconv" "strings" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" "github.com/hashicorp/boundary/internal/errors" pb "github.com/hashicorp/boundary/internal/gen/controller/api" + pberrors "github.com/hashicorp/boundary/internal/gen/errors" "github.com/hashicorp/boundary/internal/observability/event" + "github.com/mr-tron/base58" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" ) const ( genericUniquenessMsg = "Invalid request. Request attempted to make second resource with the same field value that must be unique." genericNotFoundMsg = "Unable to find requested resource." + + // domainErrHeader defines an http header for encoded domain errors from the + // grpc server. + domainErrHeader = "x-domain-err" + // domainErrMetadataHeader defines an http header for encoded domain errors from the + // grpc server via metadata + domainErrMetadataHeader = "Grpc-Metadata-X-Domain-Err" + + apiErrHeader = "x-api-err" + apiErrMetadataHeader = "Grpc-Metadata-X-Api-Err" ) -type apiError struct { - status int32 - inner *pb.Error +type ApiError struct { + Status int32 + Inner *pb.Error } -func (e *apiError) Error() string { - res := fmt.Sprintf("Status: %d, Kind: %q, Error: %q", e.status, e.inner.GetKind(), e.inner.GetMessage()) +func (e *ApiError) Error() string { + res := fmt.Sprintf("Status: %d, Kind: %q, Error: %q", e.Status, e.Inner.GetKind(), e.Inner.GetMessage()) var dets []string - for _, rf := range e.inner.GetDetails().GetRequestFields() { + for _, rf := range e.Inner.GetDetails().GetRequestFields() { dets = append(dets, fmt.Sprintf("{name: %q, desc: %q}", rf.GetName(), rf.GetDescription())) } if len(dets) > 0 { @@ -39,19 +54,19 @@ func (e *apiError) Error() string { return res } -func (e *apiError) Is(target error) bool { - var tApiErr *apiError +func (e *ApiError) Is(target error) bool { + var tApiErr *ApiError if !errors.As(target, &tApiErr) { return false } - return tApiErr.inner.Kind == e.inner.Kind && tApiErr.status == e.status + return tApiErr.Inner.Kind == e.Inner.Kind && tApiErr.Status == e.Status } // ApiErrorWithCode returns an api error with the provided code. func ApiErrorWithCode(c codes.Code) error { - return &apiError{ - status: int32(runtime.HTTPStatusFromCode(c)), - inner: &pb.Error{ + return &ApiError{ + Status: int32(runtime.HTTPStatusFromCode(c)), + Inner: &pb.Error{ Kind: c.String(), }, } @@ -59,9 +74,9 @@ func ApiErrorWithCode(c codes.Code) error { // ApiErrorWithCodeAndMessage returns an api error with the provided code and message. func ApiErrorWithCodeAndMessage(c codes.Code, msg string, args ...interface{}) error { - return &apiError{ - status: int32(runtime.HTTPStatusFromCode(c)), - inner: &pb.Error{ + return &ApiError{ + Status: int32(runtime.HTTPStatusFromCode(c)), + Inner: &pb.Error{ Kind: c.String(), Message: fmt.Sprintf(msg, args...), }, @@ -70,9 +85,9 @@ func ApiErrorWithCodeAndMessage(c codes.Code, msg string, args ...interface{}) e // NotFoundError returns an ApiError indicating a resource couldn't be found. func NotFoundError() error { - return &apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ + return &ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: codes.NotFound.String(), Message: "Resource not found.", }, @@ -80,19 +95,19 @@ func NotFoundError() error { } // NotFoundErrorf returns an ApiError indicating a resource couldn't be found. -func NotFoundErrorf(msg string, a ...interface{}) *apiError { - return &apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ +func NotFoundErrorf(msg string, a ...interface{}) *ApiError { + return &ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: codes.NotFound.String(), Message: fmt.Sprintf(msg, a...), }, } } -var unauthorizedError = &apiError{ - status: http.StatusForbidden, - inner: &pb.Error{ +var unauthorizedError = &ApiError{ + Status: http.StatusForbidden, + Inner: &pb.Error{ Kind: codes.PermissionDenied.String(), Message: "Forbidden.", }, @@ -102,9 +117,9 @@ func ForbiddenError() error { return unauthorizedError } -var unauthenticatedError = &apiError{ - status: http.StatusUnauthorized, - inner: &pb.Error{ +var unauthenticatedError = &ApiError{ + Status: http.StatusUnauthorized, + Inner: &pb.Error{ Kind: codes.Unauthenticated.String(), Message: "Unauthenticated, or invalid token.", }, @@ -114,47 +129,49 @@ func UnauthenticatedError() error { return unauthenticatedError } -func InvalidArgumentErrorf(msg string, fields map[string]string) *apiError { +func InvalidArgumentErrorf(msg string, fields map[string]string) *ApiError { const op = "handlers.InvalidArgumentErrorf" ctx := context.TODO() err := ApiErrorWithCodeAndMessage(codes.InvalidArgument, msg) - var apiErr *apiError + var apiErr *ApiError if !errors.As(err, &apiErr) { event.WriteError(ctx, op, err, event.WithInfoMsg("Unable to build invalid argument api error.")) } if len(fields) > 0 { - apiErr.inner.Details = &pb.ErrorDetails{} + apiErr.Inner.Details = &pb.ErrorDetails{} } for k, v := range fields { - apiErr.inner.Details.RequestFields = append(apiErr.inner.Details.RequestFields, &pb.FieldError{Name: k, Description: v}) + apiErr.Inner.Details.RequestFields = append(apiErr.Inner.Details.RequestFields, &pb.FieldError{Name: k, Description: v}) } - sort.Slice(apiErr.inner.GetDetails().GetRequestFields(), func(i, j int) bool { - return apiErr.inner.Details.RequestFields[i].GetName() < apiErr.inner.Details.RequestFields[j].GetName() + sort.Slice(apiErr.Inner.GetDetails().GetRequestFields(), func(i, j int) bool { + return apiErr.Inner.Details.RequestFields[i].GetName() < apiErr.Inner.Details.RequestFields[j].GetName() }) return apiErr } +var statusRegEx = regexp.MustCompile("Status: ([0-9]+), Kind: \"(.*)\", Error: \"(.*)\"") + // Converts a known errors into an error that can presented to an end user over the API. -func backendErrorToApiError(inErr error) *apiError { +func backendErrorToApiError(inErr error) *ApiError { stErr := status.Convert(inErr) switch { case errors.Is(inErr, runtime.ErrNotMatch): // grpc gateway uses this error when the path was not matched, but the error uses codes.Unimplemented which doesn't match the intention. // Overwrite the error to match our expected behavior. - return &apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ + return &ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: codes.NotFound.String(), Message: http.StatusText(http.StatusNotFound), }, } case status.Code(inErr) == codes.Unimplemented: // Instead of returning a 501 we always want to return a 405 when a method isn't implemented. - return &apiError{ - status: http.StatusMethodNotAllowed, - inner: &pb.Error{ + return &ApiError{ + Status: http.StatusMethodNotAllowed, + Inner: &pb.Error{ Kind: codes.Unimplemented.String(), Message: stErr.Message(), }, @@ -176,11 +193,23 @@ func backendErrorToApiError(inErr error) *apiError { statusCode = int32(domainErr.Code) } + // perhaps the error is from the grpc gateway, so match against the known + // apiError msg format. + if found := statusRegEx.FindStringSubmatch(stErr.Message()); len(found) == 4 { + u32, err := strconv.ParseInt(found[1], 10, 32) + if err == nil { // notice it's testing for NO err + return &ApiError{ + Status: int32(u32), + Inner: &pb.Error{Kind: found[2], Message: found[3]}, + } + } + } + // TODO: Don't return potentially sensitive information (like which user id an account // is already associated with when attempting to re-associate it). - return &apiError{ - status: statusCode, - inner: &pb.Error{Kind: codes.Internal.String(), Message: inErr.Error()}, + return &ApiError{ + Status: statusCode, + Inner: &pb.Error{Kind: codes.Internal.String(), Message: inErr.Error()}, } } @@ -189,7 +218,70 @@ func ErrorHandler() runtime.ErrorHandlerFunc { const errorFallback = `{"error": "failed to marshal error message"}` return func(ctx context.Context, _ *runtime.ServeMux, mar runtime.Marshaler, w http.ResponseWriter, r *http.Request, inErr error) { // API specified error, otherwise we need to translate repo/db errors. - var apiErr *apiError + + // the grpc server will encoded domain errors into the x-domain-err, so + // let's check there first. (see: controller.errorInterceptor) + md, ok := runtime.ServerMetadataFromContext(ctx) + if ok { + defer func() { + // make sure we don't leak the headers that were used as a comm + // channel between the grpc server and the http proxy + delete(md.HeaderMD, domainErrHeader) + delete(w.Header(), domainErrMetadataHeader) + + delete(md.HeaderMD, apiErrHeader) + delete(w.Header(), apiErrMetadataHeader) + }() + domainErrHdrs := md.HeaderMD.Get(domainErrHeader) + apiErrHdrs := md.HeaderMD.Get(apiErrHeader) + + switch { + case len(domainErrHdrs) > 0: + decoded, err := base58.FastBase58Decoding(domainErrHdrs[0]) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to decode domain err header")) + w.WriteHeader(http.StatusInternalServerError) + if _, err := io.WriteString(w, errorFallback); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to write response")) + } + return + } + var pbErr pberrors.Err + if err := proto.Unmarshal(decoded, &pbErr); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to marshal domain err header")) + w.WriteHeader(http.StatusInternalServerError) + if _, err := io.WriteString(w, errorFallback); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to write response")) + } + return + } + inErr = errors.FromPbErrors(&pbErr) + case len(apiErrHdrs) > 0: + decoded, err := base58.FastBase58Decoding(apiErrHdrs[0]) + if err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to decode api err header")) + w.WriteHeader(http.StatusInternalServerError) + if _, err := io.WriteString(w, errorFallback); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to write response")) + } + return + } + var pbApiErr pberrors.ApiError + if err := proto.Unmarshal(decoded, &pbApiErr); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to marshal api err header")) + w.WriteHeader(http.StatusInternalServerError) + if _, err := io.WriteString(w, errorFallback); err != nil { + event.WriteError(ctx, op, err, event.WithInfoMsg("failed to write response")) + } + return + } + inErr = &ApiError{ + Status: pbApiErr.Status, + Inner: pbApiErr.ApiError, + } + } + } + var apiErr *ApiError isApiErr := errors.As(inErr, &apiErr) if !isApiErr { if err := backendErrorToApiError(inErr); err != nil && !errors.As(err, &apiErr) { @@ -197,13 +289,13 @@ func ErrorHandler() runtime.ErrorHandlerFunc { } } - if apiErr.status == http.StatusInternalServerError { + if apiErr.Status == http.StatusInternalServerError { event.WriteError(ctx, op, inErr, event.WithInfoMsg("internal error returned")) } - buf, merr := mar.Marshal(apiErr.inner) + buf, merr := mar.Marshal(apiErr.Inner) if merr != nil { - event.WriteError(ctx, op, merr, event.WithInfoMsg("failed to marshal error response", "response", fmt.Sprintf("%#v", apiErr.inner))) + event.WriteError(ctx, op, merr, event.WithInfoMsg("failed to marshal error response", "response", fmt.Sprintf("%#v", apiErr.Inner))) w.WriteHeader(http.StatusInternalServerError) if _, err := io.WriteString(w, errorFallback); err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("failed to write response")) @@ -211,8 +303,8 @@ func ErrorHandler() runtime.ErrorHandlerFunc { return } - w.Header().Set("Content-Type", mar.ContentType(apiErr.inner)) - w.WriteHeader(int(apiErr.status)) + w.Header().Set("Content-Type", mar.ContentType(apiErr.Inner)) + w.WriteHeader(int(apiErr.Status)) if _, err := w.Write(buf); err != nil { event.WriteError(ctx, op, err, event.WithInfoMsg("failed to send response chunk")) return @@ -221,5 +313,5 @@ func ErrorHandler() runtime.ErrorHandlerFunc { } func ToApiError(e error) *pb.Error { - return backendErrorToApiError(e).inner + return backendErrorToApiError(e).Inner } diff --git a/internal/servers/controller/handlers/errors_test.go b/internal/servers/controller/handlers/errors_test.go index f4b2f34fc..f76a1a51b 100644 --- a/internal/servers/controller/handlers/errors_test.go +++ b/internal/servers/controller/handlers/errors_test.go @@ -32,14 +32,14 @@ func TestApiErrorHandler(t *testing.T) { testCases := []struct { name string err error - expected apiError + expected ApiError }{ { name: "Not Found", err: NotFoundErrorf("Test"), - expected: apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: "NotFound", Message: "Test", }, @@ -51,9 +51,9 @@ func TestApiErrorHandler(t *testing.T) { "k1": "v1", "k2": "v2", }), - expected: apiError{ - status: http.StatusBadRequest, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusBadRequest, + Inner: &pb.Error{ Kind: "InvalidArgument", Message: "Test", Details: &pb.ErrorDetails{ @@ -74,9 +74,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "GrpcGateway Routing Error", err: runtime.ErrNotMatch, - expected: apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: "NotFound", Message: http.StatusText(http.StatusNotFound), }, @@ -85,9 +85,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Unimplemented error", err: status.Error(codes.Unimplemented, "Test"), - expected: apiError{ - status: http.StatusMethodNotAllowed, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusMethodNotAllowed, + Inner: &pb.Error{ Kind: "Unimplemented", Message: "Test", }, @@ -96,9 +96,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Unknown error", err: stderrors.New("Some random error"), - expected: apiError{ - status: http.StatusInternalServerError, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb.Error{ Kind: "Internal", Message: "Some random error", }, @@ -107,9 +107,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db invalid public id", err: errors.E(ctx, errors.WithCode(errors.InvalidPublicId)), - expected: apiError{ - status: http.StatusInternalServerError, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb.Error{ Kind: "Internal", Message: "invalid public id, parameter violation: error #102", }, @@ -118,9 +118,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db invalid parameter", err: errors.E(ctx, errors.WithCode(errors.InvalidParameter)), - expected: apiError{ - status: http.StatusInternalServerError, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb.Error{ Kind: "Internal", Message: "invalid parameter, parameter violation: error #100", }, @@ -129,9 +129,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db invalid field mask", err: errors.E(ctx, errors.WithCode(errors.InvalidFieldMask)), - expected: apiError{ - status: http.StatusBadRequest, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusBadRequest, + Inner: &pb.Error{ Kind: "InvalidArgument", Message: "Error in provided request", Details: &pb.ErrorDetails{RequestFields: []*pb.FieldError{{Name: "update_mask", Description: "Invalid update mask provided."}}}, @@ -141,9 +141,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db empty field mask", err: errors.E(ctx, errors.WithCode(errors.EmptyFieldMask)), - expected: apiError{ - status: http.StatusBadRequest, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusBadRequest, + Inner: &pb.Error{ Kind: "InvalidArgument", Message: "Error in provided request", Details: &pb.ErrorDetails{RequestFields: []*pb.FieldError{{Name: "update_mask", Description: "Invalid update mask provided."}}}, @@ -153,9 +153,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db not unqiue", err: errors.E(ctx, errors.WithCode(errors.NotUnique)), - expected: apiError{ - status: http.StatusBadRequest, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusBadRequest, + Inner: &pb.Error{ Kind: "InvalidArgument", Message: genericUniquenessMsg, }, @@ -164,9 +164,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db record not found", err: errors.E(ctx, errors.WithCode(errors.RecordNotFound)), - expected: apiError{ - status: http.StatusNotFound, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusNotFound, + Inner: &pb.Error{ Kind: "NotFound", Message: genericNotFoundMsg, }, @@ -175,9 +175,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error Db multiple records", err: errors.E(ctx, errors.WithCode(errors.MultipleRecords)), - expected: apiError{ - status: http.StatusInternalServerError, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb.Error{ Kind: "Internal", Message: "multiple records, search issue: error #1101", }, @@ -186,9 +186,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Domain error account already associated", err: errors.E(ctx, errors.WithCode(errors.AccountAlreadyAssociated)), - expected: apiError{ - status: http.StatusBadRequest, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusBadRequest, + Inner: &pb.Error{ Kind: "InvalidArgument", Message: "account already associated with another user, parameter violation: error #114", }, @@ -197,9 +197,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Wrapped domain error", err: errors.E(ctx, errors.WithCode(errors.InvalidAddress), errors.WithMsg("test msg"), errors.WithWrap(errors.E(ctx, errors.WithCode(errors.NotNull), errors.WithMsg("inner msg")))), - expected: apiError{ - status: http.StatusInternalServerError, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb.Error{ Kind: "Internal", Message: "test msg: parameter violation: error #101: inner msg: integrity violation: error #1001", }, @@ -208,9 +208,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Forbidden domain error", err: errors.E(ctx, errors.WithCode(errors.Forbidden), errors.WithMsg("test msg")), - expected: apiError{ - status: http.StatusForbidden, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusForbidden, + Inner: &pb.Error{ Kind: "Internal", Message: "test msg: unknown: error #403", }, @@ -219,9 +219,9 @@ func TestApiErrorHandler(t *testing.T) { { name: "Wrapped forbidden domain error", err: fmt.Errorf("got error: %w", errors.E(ctx, errors.WithCode(errors.Forbidden), errors.WithMsg("test msg"))), - expected: apiError{ - status: http.StatusForbidden, - inner: &pb.Error{ + expected: ApiError{ + Status: http.StatusForbidden, + Inner: &pb.Error{ Kind: "Internal", Message: "got error: test msg: unknown: error #403", }, @@ -234,7 +234,7 @@ func TestApiErrorHandler(t *testing.T) { w := httptest.NewRecorder() tested(ctx, mux, outMarsh, w, req, tc.err) resp := w.Result() - assert.EqualValues(tc.expected.status, resp.StatusCode) + assert.EqualValues(tc.expected.Status, resp.StatusCode) got, err := ioutil.ReadAll(resp.Body) require.NoError(err) @@ -243,8 +243,8 @@ func TestApiErrorHandler(t *testing.T) { err = inMarsh.Unmarshal(got, gotErr) require.NoError(err) - assert.Equal(tc.expected.status, int32(resp.StatusCode)) - assert.Empty(cmp.Diff(tc.expected.inner, gotErr, protocmp.Transform())) + assert.Equal(tc.expected.Status, int32(resp.StatusCode)) + assert.Empty(cmp.Diff(tc.expected.Inner, gotErr, protocmp.Transform())) }) } } diff --git a/internal/servers/controller/handlers/managed_groups/managed_group_service_test.go b/internal/servers/controller/handlers/managed_groups/managed_group_service_test.go index 7c468be33..1ca2cda25 100644 --- a/internal/servers/controller/handlers/managed_groups/managed_group_service_test.go +++ b/internal/servers/controller/handlers/managed_groups/managed_group_service_test.go @@ -417,6 +417,7 @@ func TestDelete_twice(t *testing.T) { o, _ := iam.TestScopes(t, iam.TestRepo(t, conn, wrap)) databaseWrapper, err := kmsCache.GetWrapper(context.Background(), o.PublicId, kms.KeyPurposeDatabase) + require.NoError(err) oidcAm := oidc.TestAuthMethod( t, conn, databaseWrapper, o.PublicId, oidc.ActivePrivateState, diff --git a/internal/servers/controller/handlers/outgoing_interceptor_test.go b/internal/servers/controller/handlers/outgoing_interceptor_test.go deleted file mode 100644 index 798368596..000000000 --- a/internal/servers/controller/handlers/outgoing_interceptor_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package handlers - -import ( - "context" - "net/http" - "net/http/httptest" - "testing" - - pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" - pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/structpb" -) - -func TestOutgoingSplitCookie(t *testing.T) { - rec := httptest.NewRecorder() - attrs, err := ProtoToStruct(&pb.AuthToken{Token: "t_abc_1234567890"}) - require.NoError(t, err) - attrs.GetFields()["token_type"] = structpb.NewStringValue("cookie") - require.NoError(t, OutgoingInterceptor(context.Background(), rec, &pbs.AuthenticateResponse{Attributes: attrs})) - assert.ElementsMatch(t, rec.Result().Cookies(), []*http.Cookie{ - {Name: HttpOnlyCookieName, Value: "34567890", HttpOnly: true, Raw: "wt-http-token-cookie=34567890; HttpOnly"}, - {Name: JsVisibleCookieName, Value: "t_abc_12", Raw: "wt-js-token-cookie=t_abc_12"}, - }) -} diff --git a/internal/servers/controller/handlers/outgoing_interceptor.go b/internal/servers/controller/handlers/outgoing_response_filter.go similarity index 55% rename from internal/servers/controller/handlers/outgoing_interceptor.go rename to internal/servers/controller/handlers/outgoing_response_filter.go index 2814c2b10..304409775 100644 --- a/internal/servers/controller/handlers/outgoing_interceptor.go +++ b/internal/servers/controller/handlers/outgoing_response_filter.go @@ -4,30 +4,65 @@ import ( "context" "fmt" "net/http" + "strconv" "strings" + "github.com/hashicorp/boundary/internal/errors" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/observability/event" authtokenpb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" "google.golang.org/protobuf/proto" ) const ( - HttpOnlyCookieName = "wt-http-token-cookie" - JsVisibleCookieName = "wt-js-token-cookie" - tokenTypeField = "token_type" - finalRedirectUrlField = "final_redirect_url" - statusField = "status" + HttpOnlyCookieName = "wt-http-token-cookie" + JsVisibleCookieName = "wt-js-token-cookie" + tokenTypeField = "token_type" + finalRedirectUrlField = "final_redirect_url" + statusField = "status" + StatusCodeHeader = "x-http-code" + statusCodeMetadataHeader = "Grpc-Metadata-X-Http-Code" ) -func OutgoingInterceptor(ctx context.Context, w http.ResponseWriter, m proto.Message) error { - const op = "handlers.OutgoingInterceptor" - m = m.ProtoReflect().Interface() - if !m.ProtoReflect().IsValid() { - // This would be the case if it's a nil pointer - w.WriteHeader(http.StatusNoContent) - return nil +// SetStatusCode allows a grpc service handler to set the outgoing http status +// code +func SetStatusCode(ctx context.Context, code int) error { + const op = "handlers.SetHttpStatusCode" + if http.StatusText(code) == "" { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unknown status code: %d", code)) + } + if err := grpc.SetHeader(ctx, metadata.Pairs(StatusCodeHeader, strconv.Itoa(code))); err != nil { + return errors.Wrap(ctx, err, op, errors.WithCode(errors.Internal)) + } + return nil +} + +// OutgoingResponseFilter is a gRPC gateway WithForwardResponseOption. It's +// basically a filter that can manipulate the http response and has acesss to +// the outgoing proto msg +func OutgoingResponseFilter(ctx context.Context, w http.ResponseWriter, m proto.Message) error { + const op = "handlers.OutgoingResponseFilter" + + if md, ok := runtime.ServerMetadataFromContext(ctx); ok { + // set http status codes based on metadata set by the grpc service + if statusCodes := md.HeaderMD.Get(StatusCodeHeader); len(statusCodes) > 0 { + defer func() { + // delete the headers to not expose any grpc-metadata in http response + delete(md.HeaderMD, StatusCodeHeader) + delete(w.Header(), statusCodeMetadataHeader) + }() + lastStatus, err := strconv.Atoi(statusCodes[len(statusCodes)-1]) + if err != nil { + return fmt.Errorf("%s: unable to convert status code %s: %w", op, statusCodes[len(statusCodes)-1], err) + } + w.WriteHeader(lastStatus) + return nil + } } // add the outgoing resp proto. See: common.flushGatedEvents is where the diff --git a/internal/servers/controller/handlers/outgoing_response_filter_test.go b/internal/servers/controller/handlers/outgoing_response_filter_test.go new file mode 100644 index 000000000..c445329d3 --- /dev/null +++ b/internal/servers/controller/handlers/outgoing_response_filter_test.go @@ -0,0 +1,71 @@ +package handlers + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + emptypb "github.com/hashicorp/boundary/internal/gen/controller/api" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/authtokens" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestOutgoingSplitCookie(t *testing.T) { + rec := httptest.NewRecorder() + attrs, err := ProtoToStruct(&pb.AuthToken{Token: "t_abc_1234567890"}) + require.NoError(t, err) + attrs.GetFields()["token_type"] = structpb.NewStringValue("cookie") + require.NoError(t, OutgoingResponseFilter(context.Background(), rec, &pbs.AuthenticateResponse{Attributes: attrs})) + assert.ElementsMatch(t, rec.Result().Cookies(), []*http.Cookie{ + {Name: HttpOnlyCookieName, Value: "34567890", HttpOnly: true, Raw: "wt-http-token-cookie=34567890; HttpOnly"}, + {Name: JsVisibleCookieName, Value: "t_abc_12", Raw: "wt-js-token-cookie=t_abc_12"}, + }) +} + +func TestOutgoingResponseFilter_StatusCode(t *testing.T) { + tests := []struct { + name string + md runtime.ServerMetadata + msg proto.Message + wantErr bool + wantStatusCode int + wantContent bool + }{ + { + name: "204", + md: runtime.ServerMetadata{HeaderMD: metadata.Pairs(StatusCodeHeader, "204")}, + wantStatusCode: 204, + }, + { + name: "418", + md: runtime.ServerMetadata{HeaderMD: metadata.Pairs(StatusCodeHeader, "418")}, + wantStatusCode: 418, + wantContent: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + rec := httptest.NewRecorder() + ctx := runtime.NewServerMetadataContext(context.Background(), tt.md) + err := OutgoingResponseFilter(ctx, rec, &emptypb.EmptyResponse{}) + if tt.wantErr { + require.Error(err) + return + } + require.NoError(err) + resp := rec.Result() + if tt.wantStatusCode != 0 { + assert.Equal(tt.wantStatusCode, resp.StatusCode) + } + }) + } +} diff --git a/internal/servers/controller/handlers/sessions/session_service_test.go b/internal/servers/controller/handlers/sessions/session_service_test.go index 5f44e671f..4fa363167 100644 --- a/internal/servers/controller/handlers/sessions/session_service_test.go +++ b/internal/servers/controller/handlers/sessions/session_service_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/errors" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" @@ -21,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers/sessions" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/hashicorp/boundary/internal/types/scope" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/sessions" @@ -57,7 +59,7 @@ func TestGetSession(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, p.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, p.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrap, session.ComposedOf{ UserId: uId, @@ -86,7 +88,7 @@ func TestGetSession(t *testing.T) { Scope: &scopes.ScopeInfo{Id: p.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()}, States: []*pb.SessionState{{Status: session.StatusPending.String(), StartTime: sess.CreateTime.GetTimestamp()}}, Certificate: sess.Certificate, - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), AuthorizedActions: testAuthorizedActions, } @@ -174,7 +176,7 @@ func TestList_Self(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) // By default a user can read/cancel their own sessions. session.TestSession(t, conn, wrap, session.ComposedOf{ @@ -210,15 +212,15 @@ func TestList_Self(t *testing.T) { t.Run(tc.name, func(t *testing.T) { // Setup the auth request information req := httptest.NewRequest("GET", fmt.Sprintf("http://127.0.0.1/v1/sessions?scope_id=%s", pWithSessions.GetPublicId()), nil) - requestInfo := auth.RequestInfo{ + requestInfo := authpb.RequestInfo{ Path: req.URL.Path, Method: req.Method, - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: tc.requester.GetPublicId(), Token: tc.requester.GetToken(), } - ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, requestInfo) + ctx := auth.NewVerifierContext(context.Background(), iamRepoFn, tokenRepoFn, serversRepoFn, kms, &requestInfo) got, err := s.ListSessions(ctx, &pbs.ListSessionsRequest{ScopeId: pWithSessions.GetPublicId()}) require.NoError(t, err) assert.Equal(t, tc.count, len(got.GetItems()), got.GetItems()) @@ -258,13 +260,13 @@ func TestList(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, pWithSessions.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) hcOther := static.TestCatalogs(t, conn, pWithOtherSessions.GetPublicId(), 1)[0] hsOther := static.TestSets(t, conn, hcOther.GetPublicId(), 1)[0] hOther := static.TestHosts(t, conn, hcOther.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hsOther.GetPublicId(), []*static.Host{hOther}) - tarOther := target.TestTcpTarget(t, conn, pWithOtherSessions.GetPublicId(), "test", target.WithHostSources([]string{hsOther.GetPublicId()})) + tarOther := tcp.TestTarget(t, conn, pWithOtherSessions.GetPublicId(), "test", target.WithHostSources([]string{hsOther.GetPublicId()})) var wantSession []*pb.Session var totalSession []*pb.Session @@ -298,7 +300,7 @@ func TestList(t *testing.T) { Status: status, States: states, Certificate: sess.Certificate, - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), AuthorizedActions: testAuthorizedActions, }) @@ -333,7 +335,7 @@ func TestList(t *testing.T) { Status: status, States: states, Certificate: sess.Certificate, - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), AuthorizedActions: testAuthorizedActions, }) } @@ -469,7 +471,7 @@ func TestCancel(t *testing.T) { hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h}) - tar := target.TestTcpTarget(t, conn, p.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) + tar := tcp.TestTarget(t, conn, p.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()})) sess := session.TestSession(t, conn, wrap, session.ComposedOf{ UserId: uId, @@ -496,7 +498,7 @@ func TestCancel(t *testing.T) { Scope: &scopes.ScopeInfo{Id: p.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()}, Status: session.StatusCanceling.String(), Certificate: sess.Certificate, - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), AuthorizedActions: testAuthorizedActions, } diff --git a/internal/servers/controller/handlers/targets/target_service.go b/internal/servers/controller/handlers/targets/target_service.go index f1ab5844f..1b1cecdb6 100644 --- a/internal/servers/controller/handlers/targets/target_service.go +++ b/internal/servers/controller/handlers/targets/target_service.go @@ -30,7 +30,8 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/session" "github.com/hashicorp/boundary/internal/target" - "github.com/hashicorp/boundary/internal/target/store" + "github.com/hashicorp/boundary/internal/target/tcp" + tcpStore "github.com/hashicorp/boundary/internal/target/tcp/store" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" @@ -78,7 +79,10 @@ var ( func init() { var err error - if maskManager, err = handlers.NewMaskManager(handlers.MaskDestination{&store.TcpTarget{}}, handlers.MaskSource{&pb.Target{}, &pb.TcpTargetAttributes{}}); err != nil { + if maskManager, err = handlers.NewMaskManager( + handlers.MaskDestination{&tcpStore.Target{}}, + handlers.MaskSource{&pb.Target{}, &pb.TcpTargetAttributes{}}, + ); err != nil { panic(err) } } @@ -1010,7 +1014,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession // Generate the endpoint URL endpointUrl := &url.URL{ - Scheme: t.GetType(), + Scheme: t.GetType().String(), } defaultPort := t.GetDefaultPort() if defaultPort != 0 { @@ -1124,7 +1128,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession TargetId: t.GetPublicId(), Scope: authResults.Scope, CreatedTime: sess.CreateTime.GetTimestamp(), - Type: t.GetType(), + Type: t.GetType().String(), Certificate: sess.Certificate, PrivateKey: privKey, HostId: chosenEndpoint.HostId, @@ -1143,7 +1147,7 @@ func (s Service) AuthorizeSession(ctx context.Context, req *pbs.AuthorizeSession TargetId: t.GetPublicId(), Scope: authResults.Scope, CreatedTime: sess.CreateTime.GetTimestamp(), - Type: t.GetType(), + Type: t.GetType().String(), AuthorizationToken: string(encodedMarshaledSad), UserId: authResults.UserId, HostId: chosenEndpoint.HostId, @@ -1194,7 +1198,7 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Targ if tcpAttrs.GetDefaultPort().GetValue() != 0 { opts = append(opts, target.WithDefaultPort(tcpAttrs.GetDefaultPort().GetValue())) } - u, err := target.NewTcpTarget(item.GetScopeId(), opts...) + u, err := tcp.New(item.GetScopeId(), opts...) if err != nil { return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for creation: %v.", err) } @@ -1202,7 +1206,7 @@ func (s Service) createInRepo(ctx context.Context, item *pb.Target) (target.Targ if err != nil { return nil, nil, nil, err } - out, hs, cl, err := repo.CreateTcpTarget(ctx, u) + out, hs, cl, err := repo.CreateTarget(ctx, u) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target")) } @@ -1238,7 +1242,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st opts = append(opts, target.WithDefaultPort(tcpAttrs.GetDefaultPort().GetValue())) } version := item.GetVersion() - u, err := target.NewTcpTarget(scopeId, opts...) + u, err := tcp.New(scopeId, opts...) if err != nil { return nil, nil, nil, handlers.ApiErrorWithCodeAndMessage(codes.Internal, "Unable to build target for update: %v.", err) } @@ -1251,7 +1255,7 @@ func (s Service) updateInRepo(ctx context.Context, scopeId, id string, mask []st if err != nil { return nil, nil, nil, err } - out, hs, cl, rowsUpdated, err := repo.UpdateTcpTarget(ctx, u, version, dbMask) + out, hs, cl, rowsUpdated, err := repo.UpdateTarget(ctx, u, version, dbMask) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to update target")) } @@ -1476,7 +1480,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou out.ScopeId = in.GetScopeId() } if outputFields.Has(globals.TypeField) { - out.Type = target.TcpTargetType.String() + out.Type = in.GetType().String() } if outputFields.Has(globals.DescriptionField) && in.GetDescription() != "" { out.Description = wrapperspb.String(in.GetDescription()) @@ -1594,7 +1598,7 @@ func toProto(ctx context.Context, in target.Target, hostSources []target.HostSou // * All required parameters are set // * There are no conflicting parameters provided func validateGetRequest(req *pbs.GetTargetRequest) error { - return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, target.TcpTargetPrefix) + return handlers.ValidateGetRequest(handlers.NoopValidatorFn, req, tcp.TargetPrefix) } func validateCreateRequest(req *pbs.CreateTargetRequest) error { @@ -1619,7 +1623,7 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error { badFields[globals.SessionMaxSecondsField] = "This must be greater than zero." } switch target.SubtypeFromType(req.GetItem().GetType()) { - case target.TcpSubtype: + case tcp.Subtype: tcpAttrs := &pb.TcpTargetAttributes{} if err := handlers.StructToProto(req.GetItem().GetAttributes(), tcpAttrs); err != nil { badFields[globals.AttributesField] = "Attribute fields do not match the expected format." @@ -1628,11 +1632,9 @@ func validateCreateRequest(req *pbs.CreateTargetRequest) error { badFields["attributes.default_port"] = "This optional field cannot be set to 0." } } - switch req.GetItem().GetType() { - case target.TcpTargetType.String(): - case "": + if req.GetItem().GetType() == "" { badFields[globals.TypeField] = "This is a required field." - default: + } else if target.SubtypeFromType(req.GetItem().GetType()) == "" { badFields[globals.TypeField] = "Unknown type provided." } if filter := req.GetItem().GetWorkerFilter(); filter != nil { @@ -1663,8 +1665,8 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error { badFields[globals.SessionMaxSecondsField] = "This must be greater than zero." } switch target.SubtypeFromId(req.GetItem().GetType()) { - case target.TcpSubtype: - if req.GetItem().GetType() != "" && target.SubtypeFromType(req.GetItem().GetType()) != target.TcpSubtype { + case tcp.Subtype: + if req.GetItem().GetType() != "" && target.SubtypeFromType(req.GetItem().GetType()) != tcp.Subtype { badFields[globals.TypeField] = "Cannot modify the resource type." } tcpAttrs := &pb.TcpTargetAttributes{} @@ -1681,11 +1683,11 @@ func validateUpdateRequest(req *pbs.UpdateTargetRequest) error { } } return badFields - }, target.TcpTargetPrefix) + }, tcp.TargetPrefix) } func validateDeleteRequest(req *pbs.DeleteTargetRequest) error { - return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, target.TcpTargetPrefix) + return handlers.ValidateDeleteRequest(handlers.NoopValidatorFn, req, tcp.TargetPrefix) } func validateListRequest(req *pbs.ListTargetsRequest) error { @@ -1705,7 +1707,7 @@ func validateListRequest(req *pbs.ListTargetsRequest) error { func validateAddSetsRequest(req *pbs.AddTargetHostSetsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1729,7 +1731,7 @@ func validateAddSetsRequest(req *pbs.AddTargetHostSetsRequest) error { func validateSetSetsRequest(req *pbs.SetTargetHostSetsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1750,7 +1752,7 @@ func validateSetSetsRequest(req *pbs.SetTargetHostSetsRequest) error { func validateRemoveSetsRequest(req *pbs.RemoveTargetHostSetsRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1774,7 +1776,7 @@ func validateRemoveSetsRequest(req *pbs.RemoveTargetHostSetsRequest) error { func validateAddHostSourcesRequest(req *pbs.AddTargetHostSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1798,7 +1800,7 @@ func validateAddHostSourcesRequest(req *pbs.AddTargetHostSourcesRequest) error { func validateSetHostSourcesRequest(req *pbs.SetTargetHostSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1819,7 +1821,7 @@ func validateSetHostSourcesRequest(req *pbs.SetTargetHostSourcesRequest) error { func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1843,7 +1845,7 @@ func validateRemoveHostSourcesRequest(req *pbs.RemoveTargetHostSourcesRequest) e func validateAddLibrariesRequest(req *pbs.AddTargetCredentialLibrariesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1866,7 +1868,7 @@ func validateAddLibrariesRequest(req *pbs.AddTargetCredentialLibrariesRequest) e func validateSetLibrariesRequest(req *pbs.SetTargetCredentialLibrariesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1886,7 +1888,7 @@ func validateSetLibrariesRequest(req *pbs.SetTargetCredentialLibrariesRequest) e func validateRemoveLibrariesRequest(req *pbs.RemoveTargetCredentialLibrariesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1909,7 +1911,7 @@ func validateRemoveLibrariesRequest(req *pbs.RemoveTargetCredentialLibrariesRequ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1932,7 +1934,7 @@ func validateAddCredentialSourcesRequest(req *pbs.AddTargetCredentialSourcesRequ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1952,7 +1954,7 @@ func validateSetCredentialSourcesRequest(req *pbs.SetTargetCredentialSourcesRequ func validateRemoveCredentialSourcesRequest(req *pbs.RemoveTargetCredentialSourcesRequest) error { badFields := map[string]string{} - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if req.GetVersion() == 0 { @@ -1979,7 +1981,7 @@ func validateAuthorizeSessionRequest(req *pbs.AuthorizeSessionRequest) error { scopeIdEmpty := req.GetScopeId() == "" scopeNameEmpty := req.GetScopeName() == "" if nameEmpty { - if !handlers.ValidId(handlers.Id(req.GetId()), target.TcpTargetPrefix) { + if !handlers.ValidId(handlers.Id(req.GetId()), tcp.TargetPrefix) { badFields[globals.IdField] = "Incorrectly formatted identifier." } if !scopeIdEmpty { diff --git a/internal/servers/controller/handlers/targets/target_service_test.go b/internal/servers/controller/handlers/targets/target_service_test.go index 778b208b1..58df00e3b 100644 --- a/internal/servers/controller/handlers/targets/target_service_test.go +++ b/internal/servers/controller/handlers/targets/target_service_test.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/db" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" spbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/host/static" @@ -34,6 +35,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers/workers" "github.com/hashicorp/boundary/internal/session" "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" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes" @@ -112,7 +114,7 @@ func TestGet(t *testing.T) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 2) - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})) + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})) pTar := &pb.Target{ Id: tar.GetPublicId(), @@ -121,7 +123,7 @@ func TestGet(t *testing.T) { CreatedTime: tar.CreateTime.GetTimestamp(), UpdatedTime: tar.UpdateTime.GetTimestamp(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: o.GetPublicId()}, - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), HostSetIds: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, HostSourceIds: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, Attributes: new(structpb.Struct), @@ -147,7 +149,7 @@ func TestGet(t *testing.T) { }, { name: "Get a non existing Target", - req: &pbs.GetTargetRequest{Id: target.TcpTargetPrefix + "_DoesntExis"}, + req: &pbs.GetTargetRequest{Id: tcp.TargetPrefix + "_DoesntExis"}, res: nil, err: handlers.ApiErrorWithCode(codes.NotFound), }, @@ -159,7 +161,7 @@ func TestGet(t *testing.T) { }, { name: "space in id", - req: &pbs.GetTargetRequest{Id: target.TcpTargetPrefix + "_1 23456789"}, + req: &pbs.GetTargetRequest{Id: tcp.TargetPrefix + "_1 23456789"}, res: nil, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, @@ -207,7 +209,7 @@ func TestList(t *testing.T) { var totalTars []*pb.Target for i := 0; i < 5; i++ { name := fmt.Sprintf("tar%d", i) - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), name, target.WithHostSources([]string{hss[0].GetPublicId(), hss[1].GetPublicId()})) + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), name, target.WithHostSources([]string{hss[0].GetPublicId(), hss[1].GetPublicId()})) wantTars = append(wantTars, &pb.Target{ Id: tar.GetPublicId(), ScopeId: proj.GetPublicId(), @@ -216,14 +218,14 @@ func TestList(t *testing.T) { CreatedTime: tar.GetCreateTime().GetTimestamp(), UpdatedTime: tar.GetUpdateTime().GetTimestamp(), Version: tar.GetVersion(), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: new(structpb.Struct), SessionMaxSeconds: wrapperspb.UInt32(28800), SessionConnectionLimit: wrapperspb.Int32(1), AuthorizedActions: testAuthorizedActions, }) totalTars = append(totalTars, wantTars[i]) - tar = target.TestTcpTarget(t, conn, otherProj.GetPublicId(), name, target.WithHostSources([]string{otherHss[0].GetPublicId(), otherHss[1].GetPublicId()})) + tar = tcp.TestTarget(t, conn, otherProj.GetPublicId(), name, target.WithHostSources([]string{otherHss[0].GetPublicId(), otherHss[1].GetPublicId()})) totalTars = append(totalTars, &pb.Target{ Id: tar.GetPublicId(), ScopeId: otherProj.GetPublicId(), @@ -232,7 +234,7 @@ func TestList(t *testing.T) { CreatedTime: tar.GetCreateTime().GetTimestamp(), UpdatedTime: tar.GetUpdateTime().GetTimestamp(), Version: tar.GetVersion(), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: new(structpb.Struct), SessionMaxSeconds: wrapperspb.UInt32(28800), SessionConnectionLimit: wrapperspb.Int32(1), @@ -322,7 +324,7 @@ func TestDelete(t *testing.T) { } _, proj := iam.TestScopes(t, iamRepo) - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") s, err := testService(t, conn, kms, wrapper) require.NoError(t, err, "Couldn't create a new target service.") @@ -345,7 +347,7 @@ func TestDelete(t *testing.T) { name: "Delete Not Existing Target", scopeId: proj.GetPublicId(), req: &pbs.DeleteTargetRequest{ - Id: target.TcpTargetPrefix + "_doesntexis", + Id: tcp.TargetPrefix + "_doesntexis", }, err: handlers.ApiErrorWithCode(codes.NotFound), }, @@ -353,7 +355,7 @@ func TestDelete(t *testing.T) { name: "Bad target id formatting", scopeId: proj.GetPublicId(), req: &pbs.DeleteTargetRequest{ - Id: target.TcpTargetPrefix + "_bad_format", + Id: tcp.TargetPrefix + "_bad_format", }, err: handlers.ApiErrorWithCode(codes.InvalidArgument), }, @@ -385,7 +387,7 @@ func TestDelete_twice(t *testing.T) { _, proj := iam.TestScopes(t, iamRepo) - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") s, err := testService(t, conn, kms, wrapper) require.NoError(err, "Couldn't create a new target service.") @@ -425,20 +427,20 @@ func TestCreate(t *testing.T) { ScopeId: proj.GetPublicId(), Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, WorkerFilter: wrapperspb.String(`type == "bar"`), }}, res: &pbs.CreateTargetResponse{ - Uri: fmt.Sprintf("targets/%s_", target.TcpTargetPrefix), + Uri: fmt.Sprintf("targets/%s_", tcp.TargetPrefix), Item: &pb.Target{ ScopeId: proj.GetPublicId(), Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, @@ -454,7 +456,7 @@ func TestCreate(t *testing.T) { req: &pbs.CreateTargetRequest{Item: &pb.Target{ Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(0), }}, @@ -528,7 +530,7 @@ func TestCreate(t *testing.T) { if got != nil { assert.Contains(got.GetUri(), tc.res.GetUri()) - assert.True(strings.HasPrefix(got.GetItem().GetId(), target.TcpTargetPrefix), got.GetItem().GetId()) + assert.True(strings.HasPrefix(got.GetItem().GetId(), tcp.TargetPrefix), got.GetItem().GetId()) // Clear all values which are hard to compare against. got.Uri, tc.res.Uri = "", "" @@ -576,18 +578,18 @@ func TestUpdate(t *testing.T) { {Id: hs[1].GetPublicId(), HostCatalogId: hs[1].GetCatalogId()}, } - tar, err := target.NewTcpTarget(proj.GetPublicId(), target.WithName("default"), target.WithDescription("default")) + tar, err := tcp.New(proj.GetPublicId(), target.WithName("default"), target.WithDescription("default")) tar.DefaultPort = 2 require.NoError(t, err) - gtar, _, _, err := repo.CreateTcpTarget(context.Background(), tar, target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})) + gtar, _, _, err := repo.CreateTarget(context.Background(), tar, target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})) require.NoError(t, err) - tar = gtar.(*target.TcpTarget) + tar = gtar.(*tcp.Target) var version uint32 = 1 resetTarget := func() { version++ - _, _, _, _, err = repo.UpdateTcpTarget(context.Background(), tar, version, []string{"Name", "Description"}) + _, _, _, _, err = repo.UpdateTarget(context.Background(), tar, version, []string{"Name", "Description"}) require.NoError(t, err, "Failed to reset target.") version++ } @@ -617,7 +619,7 @@ func TestUpdate(t *testing.T) { Description: wrapperspb.String("desc"), SessionMaxSeconds: wrapperspb.UInt32(3600), SessionConnectionLimit: wrapperspb.Int32(5), - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), }, }, res: &pbs.UpdateTargetResponse{ @@ -627,7 +629,7 @@ func TestUpdate(t *testing.T) { Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, @@ -651,7 +653,7 @@ func TestUpdate(t *testing.T) { Item: &pb.Target{ Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), - Type: target.TcpSubtype.String(), + Type: tcp.Subtype.String(), }, }, res: &pbs.UpdateTargetResponse{ @@ -662,7 +664,7 @@ func TestUpdate(t *testing.T) { Name: wrapperspb.String("name"), Description: wrapperspb.String("desc"), CreatedTime: tar.GetCreateTime().GetTimestamp(), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, @@ -749,7 +751,7 @@ func TestUpdate(t *testing.T) { Scope: &scopes.ScopeInfo{Id: proj.GetPublicId(), Type: scope.Project.String(), ParentScopeId: org.GetPublicId()}, Name: wrapperspb.String("default"), CreatedTime: tar.GetCreateTime().GetTimestamp(), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, @@ -782,7 +784,7 @@ func TestUpdate(t *testing.T) { Name: wrapperspb.String("updated"), Description: wrapperspb.String("default"), CreatedTime: tar.GetCreateTime().GetTimestamp(), - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, @@ -818,7 +820,7 @@ func TestUpdate(t *testing.T) { Attributes: &structpb.Struct{Fields: map[string]*structpb.Value{ "default_port": structpb.NewNumberValue(2), }}, - Type: target.TcpTargetType.String(), + Type: tcp.Subtype.String(), HostSetIds: hsIds, HostSets: hostSets, HostSourceIds: hostSourceIds, @@ -832,7 +834,7 @@ func TestUpdate(t *testing.T) { { name: "Update a Non Existing Target", req: &pbs.UpdateTargetRequest{ - Id: target.TcpTargetPrefix + "_DoesntExis", + Id: tcp.TargetPrefix + "_DoesntExis", UpdateMask: &field_mask.FieldMask{ Paths: []string{"description"}, }, @@ -944,10 +946,10 @@ func TestUpdate_BadVersion(t *testing.T) { repo, err := repoFn() require.NoError(t, err, "Couldn't create new target repo.") - tar, err := target.NewTcpTarget(proj.GetPublicId(), target.WithName("default"), target.WithDescription("default")) + tar, err := tcp.New(proj.GetPublicId(), target.WithName("default"), target.WithDescription("default")) tar.DefaultPort = 2 require.NoError(t, err) - gtar, _, _, err := repo.CreateTcpTarget(context.Background(), tar) + gtar, _, _, err := repo.CreateTarget(context.Background(), tar) require.NoError(t, err) tested, err := testService(t, conn, kms, wrapper) @@ -992,37 +994,37 @@ func TestAddTargetHostSets(t *testing.T) { addCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target addHostSets []string resultHostSets []string }{ { name: "Add set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), addHostSets: []string{hs[1].GetPublicId()}, resultHostSets: []string{hs[1].GetPublicId()}, }, { name: "Add set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSets: []string{hs[1].GetPublicId()}, resultHostSets: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, { name: "Add duplicated sets on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSets: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSets: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, { name: "Add plugin set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin empty"), addHostSets: []string{pluginHs.GetPublicId()}, resultHostSets: []string{pluginHs.GetPublicId()}, }, { name: "Add plugin set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSets: []string{pluginHs.GetPublicId()}, resultHostSets: []string{hs[0].GetPublicId(), pluginHs.GetPublicId()}, }, @@ -1045,7 +1047,7 @@ func TestAddTargetHostSets(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") failCases := []struct { name string @@ -1126,43 +1128,43 @@ func TestSetTargetHostSets(t *testing.T) { setCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target setHostSets []string resultHostSets []string }{ { name: "Set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), setHostSets: []string{hs[1].GetPublicId()}, resultHostSets: []string{hs[1].GetPublicId()}, }, { name: "Set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSets: []string{hs[1].GetPublicId()}, resultHostSets: []string{hs[1].GetPublicId()}, }, { name: "Set duplicate host set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSets: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSets: []string{hs[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "another populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSets: []string{}, resultHostSets: nil, }, { name: "Set plugin set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin empty"), setHostSets: []string{pluginHs.GetPublicId()}, resultHostSets: []string{pluginHs.GetPublicId()}, }, { name: "Set plugin set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSets: []string{pluginHs.GetPublicId()}, resultHostSets: []string{pluginHs.GetPublicId()}, }, @@ -1181,7 +1183,7 @@ func TestSetTargetHostSets(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test name") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test name") failCases := []struct { name string @@ -1254,38 +1256,38 @@ func TestRemoveTargetHostSets(t *testing.T) { removeCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target removeHosts []string resultHosts []string wantErr bool }{ { name: "Remove from empty", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), removeHosts: []string{hs[1].GetPublicId()}, wantErr: true, }, { name: "Remove 1 of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHosts: []string{hs[1].GetPublicId()}, resultHosts: []string{hs[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHosts: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHosts: []string{hs[0].GetPublicId()}, }, { name: "Remove all hosts from set", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove all", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHosts: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, resultHosts: []string{}, }, { name: "Remove 1 plugin of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), removeHosts: []string{pluginHs.GetPublicId()}, resultHosts: []string{hs[0].GetPublicId()}, }, @@ -1312,7 +1314,7 @@ func TestRemoveTargetHostSets(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "testing") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "testing") failCases := []struct { name string @@ -1394,37 +1396,37 @@ func TestAddTargetHostSources(t *testing.T) { addCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target addHostSources []string resultHostSources []string }{ { name: "Add set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), addHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[1].GetPublicId()}, }, { name: "Add set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, { name: "Add duplicated sets on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, }, { name: "Add plugin set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin empty"), addHostSources: []string{pluginHs.GetPublicId()}, resultHostSources: []string{pluginHs.GetPublicId()}, }, { name: "Add plugin set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), addHostSources: []string{pluginHs.GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId(), pluginHs.GetPublicId()}, }, @@ -1447,7 +1449,7 @@ func TestAddTargetHostSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") failCases := []struct { name string @@ -1528,37 +1530,37 @@ func TestSetTargetHostSources(t *testing.T) { setCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target setHostSources []string resultHostSources []string }{ { name: "Set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), setHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[1].GetPublicId()}, }, { name: "Set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[1].GetPublicId()}, }, { name: "Set plugin set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "plugin populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSources: []string{pluginHs.GetPublicId()}, resultHostSources: []string{pluginHs.GetPublicId()}, }, { name: "Set duplicate host set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSources: []string{hs[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "another populated", target.WithHostSources([]string{hs[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithHostSources([]string{hs[0].GetPublicId()})), setHostSources: []string{}, resultHostSources: nil, }, @@ -1577,7 +1579,7 @@ func TestSetTargetHostSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test name") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test name") failCases := []struct { name string @@ -1650,38 +1652,38 @@ func TestRemoveTargetHostSources(t *testing.T) { removeCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target removeHostSources []string resultHostSources []string wantErr bool }{ { name: "Remove from empty", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), removeHostSources: []string{hs[1].GetPublicId()}, wantErr: true, }, { name: "Remove 1 of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHostSources: []string{hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId()}, }, { name: "Remove 1 plugin set of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove plugin partial", target.WithHostSources([]string{hs[0].GetPublicId(), pluginHs.GetPublicId()})), removeHostSources: []string{pluginHs.GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 sets", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHostSources: []string{hs[1].GetPublicId(), hs[1].GetPublicId()}, resultHostSources: []string{hs[0].GetPublicId()}, }, { name: "Remove all hosts from set", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove all", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithHostSources([]string{hs[0].GetPublicId(), hs[1].GetPublicId()})), removeHostSources: []string{hs[0].GetPublicId(), hs[1].GetPublicId()}, resultHostSources: []string{}, }, @@ -1708,7 +1710,7 @@ func TestRemoveTargetHostSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "testing") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "testing") failCases := []struct { name string @@ -1784,25 +1786,25 @@ func TestAddTargetLibraries(t *testing.T) { addCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target addLibraries []string resultLibraryIds []string }{ { name: "Add library on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty for libraries"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty for libraries"), addLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Add library on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), addLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add duplicated libraries on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for libraries", target.WithCredentialSources([]string{cls[0].GetPublicId()})), addLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultLibraryIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, @@ -1833,7 +1835,7 @@ func TestAddTargetLibraries(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") failCases := []struct { name string @@ -1919,32 +1921,32 @@ func TestSetTargetLibraries(t *testing.T) { setCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target setLibraries []string resultLibraryIds []string resultLibraries []*pb.CredentialLibrary }{ { name: "Set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), setLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setLibraries: []string{cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Set duplicate libraries on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setLibraries: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultLibraryIds: []string{cls[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setLibraries: []string{}, resultLibraryIds: nil, }, @@ -1978,7 +1980,7 @@ func TestSetTargetLibraries(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test name") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test name") failCases := []struct { name string @@ -2045,26 +2047,26 @@ func TestRemoveTargetLibraries(t *testing.T) { removeCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target removeLibs []string resultLibIds []string wantErr bool }{ { name: "Remove from empty", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), removeLibs: []string{cls[1].GetPublicId()}, wantErr: true, }, { name: "Remove 1 of 2 libraries", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeLibs: []string{cls[1].GetPublicId()}, resultLibIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 libraries", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeLibs: []string{ cls[1].GetPublicId(), cls[1].GetPublicId(), }, @@ -2072,7 +2074,7 @@ func TestRemoveTargetLibraries(t *testing.T) { }, { name: "Remove all libraries from target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeLibs: []string{ cls[0].GetPublicId(), cls[1].GetPublicId(), }, @@ -2099,7 +2101,7 @@ func TestRemoveTargetLibraries(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "testing") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "testing") failCases := []struct { name string @@ -2181,25 +2183,25 @@ func TestAddTargetSources(t *testing.T) { addCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target addSources []string resultSourceIds []string }{ { name: "Add source on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty for sources"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty for sources"), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Add source on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), addSources: []string{cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, { name: "Add duplicated sources on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicated for sources", target.WithCredentialSources([]string{cls[0].GetPublicId()})), addSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultSourceIds: []string{cls[0].GetPublicId(), cls[1].GetPublicId()}, }, @@ -2230,7 +2232,7 @@ func TestAddTargetSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") failCases := []struct { name string @@ -2316,32 +2318,32 @@ func TestSetTargetCredentialSources(t *testing.T) { setCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target setCredentialSources []string resultCredentialSourceIds []string resultCredentialSources []*pb.CredentialLibrary }{ { name: "Set on empty target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Set on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Set duplicate sources on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setCredentialSources: []string{cls[1].GetPublicId(), cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[1].GetPublicId()}, }, { name: "Set empty on populated target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "another populated", target.WithCredentialSources([]string{cls[0].GetPublicId()})), setCredentialSources: []string{}, resultCredentialSourceIds: nil, }, @@ -2375,7 +2377,7 @@ func TestSetTargetCredentialSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "test name") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test name") failCases := []struct { name string @@ -2442,26 +2444,26 @@ func TestRemoveTargetCredentialSources(t *testing.T) { removeCases := []struct { name string - tar *target.TcpTarget + tar *tcp.Target removeCredentialSources []string resultCredentialSourceIds []string wantErr bool }{ { name: "Remove from empty", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "empty"), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "empty"), removeCredentialSources: []string{cls[1].GetPublicId()}, wantErr: true, }, { name: "Remove 1 of 2 sources", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove partial", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeCredentialSources: []string{cls[1].GetPublicId()}, resultCredentialSourceIds: []string{cls[0].GetPublicId()}, }, { name: "Remove 1 duplicate set of 2 sources", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove duplicate", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeCredentialSources: []string{ cls[1].GetPublicId(), cls[1].GetPublicId(), }, @@ -2469,7 +2471,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) { }, { name: "Remove all sources from target", - tar: target.TestTcpTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), + tar: tcp.TestTarget(t, conn, proj.GetPublicId(), "remove all", target.WithCredentialSources([]string{cls[0].GetPublicId(), cls[1].GetPublicId()})), removeCredentialSources: []string{ cls[0].GetPublicId(), cls[1].GetPublicId(), }, @@ -2496,7 +2498,7 @@ func TestRemoveTargetCredentialSources(t *testing.T) { }) } - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), "testing") + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "testing") failCases := []struct { name string @@ -2622,9 +2624,9 @@ func TestAuthorizeSession(t *testing.T) { atRepoFn, serversRepoFn, kms, - auth.RequestInfo{ + &authpb.RequestInfo{ Token: at.GetToken(), - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: at.GetPublicId(), }) @@ -2635,6 +2637,7 @@ func TestAuthorizeSession(t *testing.T) { s, err := targets.NewService(ctx, kms, repoFn, iamRepoFn, serversRepoFn, sessionRepoFn, pluginHostRepoFn, staticHostRepoFn, credentialRepoFn) require.NoError(t, err) + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), "test") hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] shs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -2687,7 +2690,6 @@ func TestAuthorizeSession(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), tc.name) apiTar, err := s.AddTargetHostSets(ctx, &pbs.AddTargetHostSetsRequest{ Id: tar.GetPublicId(), Version: tar.GetVersion(), @@ -2834,9 +2836,9 @@ func TestAuthorizeSession_Errors(t *testing.T) { atRepoFn, serversRepoFn, kms, - auth.RequestInfo{ + &authpb.RequestInfo{ Token: at.GetToken(), - TokenFormat: auth.AuthTokenTypeBearer, + TokenFormat: uint32(auth.AuthTokenTypeBearer), PublicId: at.GetPublicId(), }) r := iam.TestRole(t, conn, proj.GetPublicId()) @@ -2848,7 +2850,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { sec, tok := v.CreateToken(t, vault.WithPolicies([]string{"default", "database"})) store := vault.TestCredentialStore(t, conn, wrapper, proj.GetPublicId(), v.Addr, tok, sec.Auth.Accessor) - workerExists := func(tar *target.TcpTarget) (version uint32) { + workerExists := func(tar *tcp.Target) (version uint32) { workerService := workers.NewWorkerServiceServer(serversRepoFn, sessionRepoFn, &sync.Map{}, kms) _, err := workerService.Status(context.Background(), &spbs.StatusRequest{ Worker: &spb.Server{ @@ -2860,7 +2862,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return tar.GetVersion() } - hostSetNoHostExists := func(tar *target.TcpTarget) (version uint32) { + hostSetNoHostExists := func(tar *tcp.Target) (version uint32) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -2873,7 +2875,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return tr.GetItem().GetVersion() } - hostExists := func(tar *target.TcpTarget) (version uint32) { + hostExists := func(tar *tcp.Target) (version uint32) { hc := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)[0] h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0] hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0] @@ -2887,7 +2889,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return apiTar.GetItem().GetVersion() } - libraryExists := func(tar *target.TcpTarget) (version uint32) { + libraryExists := func(tar *tcp.Target) (version uint32) { credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) require.NoError(t, err) clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ @@ -2909,7 +2911,7 @@ func TestAuthorizeSession_Errors(t *testing.T) { return tr.GetItem().GetVersion() } - misConfiguredlibraryExists := func(tar *target.TcpTarget) (version uint32) { + misConfiguredlibraryExists := func(tar *tcp.Target) (version uint32) { credService, err := credentiallibraries.NewService(credentialRepoFn, iamRepoFn) require.NoError(t, err) clsResp, err := credService.CreateCredentialLibrary(ctx, &pbs.CreateCredentialLibraryRequest{Item: &credpb.CredentialLibrary{ @@ -2933,33 +2935,33 @@ func TestAuthorizeSession_Errors(t *testing.T) { cases := []struct { name string - setup []func(*target.TcpTarget) uint32 + setup []func(*tcp.Target) uint32 err bool }{ { // This one must be run first since it relies on the DB not having any worker details name: "no worker", - setup: []func(tcpTarget *target.TcpTarget) uint32{hostExists, libraryExists}, + setup: []func(tcpTarget *tcp.Target) uint32{hostExists, libraryExists}, err: true, }, { name: "success", - setup: []func(tcpTarget *target.TcpTarget) uint32{workerExists, hostExists, libraryExists}, + setup: []func(tcpTarget *tcp.Target) uint32{workerExists, hostExists, libraryExists}, }, { name: "no hosts", - setup: []func(tcpTarget *target.TcpTarget) uint32{workerExists, hostSetNoHostExists, libraryExists}, + setup: []func(tcpTarget *tcp.Target) uint32{workerExists, hostSetNoHostExists, libraryExists}, err: true, }, { name: "bad library configuration", - setup: []func(tcpTarget *target.TcpTarget) uint32{workerExists, hostExists, misConfiguredlibraryExists}, + setup: []func(tcpTarget *tcp.Target) uint32{workerExists, hostExists, misConfiguredlibraryExists}, err: true, }, } for i, tc := range cases { t.Run(tc.name, func(t *testing.T) { - tar := target.TestTcpTarget(t, conn, proj.GetPublicId(), fmt.Sprintf("test-%d", i)) + tar := tcp.TestTarget(t, conn, proj.GetPublicId(), fmt.Sprintf("test-%d", i)) for _, fn := range tc.setup { ver := fn(tar) diff --git a/internal/servers/controller/handlers/verifiers.go b/internal/servers/controller/handlers/verifiers.go index 5481c62ab..4d394ee07 100644 --- a/internal/servers/controller/handlers/verifiers.go +++ b/internal/servers/controller/handlers/verifiers.go @@ -183,7 +183,7 @@ func ValidId(i Id, prefixes ...string) bool { continue } id = strings.TrimPrefix(id, prefix) - if !reInvalidID.Match([]byte(id)) { + if !reInvalidID.MatchString(id) { return true } } diff --git a/internal/servers/controller/handlers/verifiers_test.go b/internal/servers/controller/handlers/verifiers_test.go index d1717c2d6..7fd291b12 100644 --- a/internal/servers/controller/handlers/verifiers_test.go +++ b/internal/servers/controller/handlers/verifiers_test.go @@ -15,10 +15,10 @@ import ( func errorIncludesFields(t *testing.T, err error, wantFields []string) { t.Helper() - var apiErr *apiError + var apiErr *ApiError require.True(t, errors.As(err, &apiErr)) var gotFields []string - for _, d := range apiErr.inner.GetDetails().GetRequestFields() { + for _, d := range apiErr.Inner.GetDetails().GetRequestFields() { gotFields = append(gotFields, d.GetName()) } assert.ElementsMatch(t, gotFields, wantFields) diff --git a/internal/servers/controller/interceptor.go b/internal/servers/controller/interceptor.go new file mode 100644 index 000000000..4dfaeab98 --- /dev/null +++ b/internal/servers/controller/interceptor.go @@ -0,0 +1,207 @@ +package controller + +import ( + "context" + "fmt" + "net/http" + "reflect" + + "github.com/hashicorp/boundary/internal/errors" + pb "github.com/hashicorp/boundary/internal/gen/controller/api" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" + pberrors "github.com/hashicorp/boundary/internal/gen/errors" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/requests" + "github.com/hashicorp/boundary/internal/servers/controller/auth" + "github.com/hashicorp/boundary/internal/servers/controller/common" + "github.com/hashicorp/boundary/internal/servers/controller/handlers" + + "github.com/mr-tron/base58" + + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +const ( + requestInfoMdKey = "request-info" + + // domainErrHeader defines an http header for encoded domain errors from the + // grpc server. + domainErrHeader = "x-domain-err" + + // apiErrHeader defines an http header for encoded api errors from the + // grpc server. + apiErrHeader = "x-api-err" +) + +// requestCtxInterceptor creates an unary server interceptor that pulls grpc +// metadata into a ctx for the request. The metadata must be set in an upstream +// http handler/middleware by marshalling a RequestInfo protobuf into the +// requestInfoMdKey header (see: controller.wrapHandlerWithCommonFuncs). +func requestCtxInterceptor( + ctx context.Context, + iamRepoFn common.IamRepoFactory, + authTokenRepoFn common.AuthTokenRepoFactory, + serversRepoFn common.ServersRepoFactory, + kms *kms.Kms, + ticket string, +) (grpc.UnaryServerInterceptor, error) { + const op = "controller.requestCtxInterceptor" + if iamRepoFn == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing iam repo function") + } + if authTokenRepoFn == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing auth token repo function") + } + if serversRepoFn == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing servers repo function") + } + if kms == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing kms") + } + if ticket == "" { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing ticket") + } + // Authorization unary interceptor function to handle authorize per RPC call + return func(interceptorCtx context.Context, + req interface{}, + _ *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + md, ok := metadata.FromIncomingContext(interceptorCtx) + if !ok { + return nil, errors.New(interceptorCtx, errors.Internal, op, "No metadata") + } + + values := md.Get(requestInfoMdKey) + if len(values) == 0 { + return nil, errors.New(interceptorCtx, errors.Internal, op, "Missing request metadata") + } + if len(values) > 1 { + return nil, errors.New(interceptorCtx, errors.Internal, op, fmt.Sprintf("expected 1 value for %s metadata and got %d", requestInfoMdKey, len(values))) + } + + decoded, err := base58.FastBase58Decoding(values[0]) + if err != nil { + return nil, errors.Wrap(interceptorCtx, err, op, errors.WithCode(errors.Internal), errors.WithMsg("unable to decode request info")) + } + var requestInfo authpb.RequestInfo + if err := proto.Unmarshal(decoded, &requestInfo); err != nil { + return nil, errors.Wrap(interceptorCtx, err, op, errors.WithCode(errors.Internal), errors.WithMsg("unable to unmarshal request info")) + } + switch { + case requestInfo.Ticket == "": + return nil, errors.New(interceptorCtx, errors.Internal, op, "Invalid context (missing ticket)") + case requestInfo.Ticket != ticket: + return nil, errors.New(interceptorCtx, errors.Internal, op, "Invalid context (bad ticket)") + } + + interceptorCtx = auth.NewVerifierContext(interceptorCtx, iamRepoFn, authTokenRepoFn, serversRepoFn, kms, &requestInfo) + + // Add general request information to the context. The information from + // the auth verifier context is pretty specifically curated to + // authentication/authorization verification so this is more + // general-purpose. + // + // We could use requests.NewRequestContext but this saves an immediate + // lookup. + interceptorCtx = context.WithValue(interceptorCtx, requests.ContextRequestInformationKey, &requests.RequestContext{ + Path: requestInfo.Path, + Method: requestInfo.Method, + }) + + // Calls the handler + h, err := handler(interceptorCtx, req) + + return h, err // not convinced we want to wrap every error and turn them into domain errors... + }, nil +} + +func errorInterceptor( + _ context.Context, +) grpc.UnaryServerInterceptor { + const op = "controller.errorInterceptor" + return func(interceptorCtx context.Context, + req interface{}, + _ *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + + // call the handler... + h, handlerErr := handler(interceptorCtx, req) + + // if there's an error and it's a domain err, then encode it into the + if handlerErr != nil { + var domainErr *errors.Err + isDomainErr := errors.As(handlerErr, &domainErr) + if isDomainErr { + pbErr := errors.ToPbErrors(domainErr) + var buf []byte + var err error + if buf, err = proto.Marshal(pbErr); err != nil { + return h, status.Errorf(codes.Internal, "unable to marshal domain error: %s", err) + } + if err := grpc.SetHeader(interceptorCtx, metadata.Pairs(domainErrHeader, base58.FastBase58Encoding(buf))); err != nil { + return h, status.Errorf(codes.Internal, "unable to set domain error header: %s", err) + } + return h, handlerErr + } + + var apiErr *handlers.ApiError + isApiError := errors.As(handlerErr, &apiErr) + if isApiError { + pbErr := &pberrors.ApiError{ + ApiError: apiErr.Inner, + Status: apiErr.Status, + } + var buf []byte + var err error + if buf, err = proto.Marshal(pbErr); err != nil { + return h, status.Errorf(codes.Internal, "unable to marshal api error: %s", err) + } + if err := grpc.SetHeader(interceptorCtx, metadata.Pairs(apiErrHeader, base58.FastBase58Encoding(buf))); err != nil { + return h, status.Errorf(codes.Internal, "unable to set api error header: %s", err) + } + return h, handlerErr + } + } + return h, handlerErr + } +} + +func statusCodeInterceptor( + _ context.Context, +) grpc.UnaryServerInterceptor { + const op = "controller.statusCodeInterceptor" + return func(interceptorCtx context.Context, + req interface{}, + _ *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + + // call the handler... + h, handlerErr := handler(interceptorCtx, req) + + // if a service handler returns nil, nil then we want to single a 204 + // response to the proxy with no resp msg + if isNil(h) && handlerErr == nil { + if err := handlers.SetStatusCode(interceptorCtx, http.StatusNoContent); err != nil { + return &pb.EmptyResponse{}, err + } + return &pb.EmptyResponse{}, nil + } + + return h, handlerErr + } +} + +func isNil(i interface{}) bool { + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + } + return false +} diff --git a/internal/servers/controller/interceptor_test.go b/internal/servers/controller/interceptor_test.go new file mode 100644 index 000000000..f970022c0 --- /dev/null +++ b/internal/servers/controller/interceptor_test.go @@ -0,0 +1,453 @@ +package controller + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "strconv" + "testing" + + "github.com/hashicorp/boundary/internal/authtoken" + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + pb_api "github.com/hashicorp/boundary/internal/gen/controller/api" + authpb "github.com/hashicorp/boundary/internal/gen/controller/auth" + pberrors "github.com/hashicorp/boundary/internal/gen/errors" + "github.com/hashicorp/boundary/internal/gen/testing/interceptor" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/servers" + "github.com/hashicorp/boundary/internal/servers/controller/auth" + "github.com/hashicorp/boundary/internal/servers/controller/common" + "github.com/hashicorp/boundary/internal/servers/controller/handlers" + "github.com/mr-tron/base58" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/proto" +) + +func Test_requestCtxInterceptor(t *testing.T) { + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + kmsCache := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + + iamRepoFn := func() (*iam.Repository, error) { + return iamRepo, nil + } + atRepoFn := func() (*authtoken.Repository, error) { + return authtoken.NewRepository(rw, rw, kmsCache) + } + serversRepoFn := func() (*servers.Repository, error) { + return servers.NewRepository(rw, rw, kmsCache) + } + + validGatewayTicket := "valid-ticket" + + o, _ := iam.TestScopes(t, iamRepo) + at := authtoken.TestAuthToken(t, conn, kmsCache, o.GetPublicId()) + encToken, err := authtoken.EncryptToken(context.Background(), kmsCache, o.GetPublicId(), at.GetPublicId(), at.GetToken()) + require.NoError(t, err) + tokValue := at.GetPublicId() + "_" + encToken + + newReqCtx := func(gwTicket string) context.Context { + req := httptest.NewRequest("GET", "http://127.0.0.1/v1/scopes/o_1", nil) + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", tokValue)) + // Add values for authn/authz checking + requestInfo := authpb.RequestInfo{ + Path: req.URL.Path, + Method: req.Method, + } + requestInfo.PublicId, requestInfo.EncryptedToken, requestInfo.TokenFormat = auth.GetTokenFromRequest(context.TODO(), kmsCache, req) + requestInfo.Ticket = gwTicket // allows the grpc-gateway to verify the request info came from it's in-memory companion http proxy + marshalledRequestInfo, err := proto.Marshal(&requestInfo) + require.NoError(t, err) + md := metadata.Pairs(requestInfoMdKey, base58.FastBase58Encoding(marshalledRequestInfo)) + mdCtx := metadata.NewIncomingContext(context.Background(), md) + + md, ok := metadata.FromIncomingContext(mdCtx) + require.True(t, ok) + require.NotNil(t, md) + + return mdCtx + } + + factoryCtx := context.Background() + + returnCtxHandler := func(ctx context.Context, req interface{}) (interface{}, error) { + return ctx, nil + } + + tests := []struct { + name string + requestCtx context.Context + iamRepoFn common.IamRepoFactory + authTokenRepoFn common.AuthTokenRepoFactory + serversRepoFn common.ServersRepoFactory + kms *kms.Kms + ticket string + wantFactoryErr bool + wantFactoryErrMatch *errors.Template + wantFactoryErrContains string + wantRequestErr bool + wantRequestErrMatch *errors.Template + wantRequestErrContains string + }{ + { + name: "missing-iam-repo", + requestCtx: newReqCtx(validGatewayTicket), + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing iam repo", + }, + { + name: "missing-at-repo", + requestCtx: newReqCtx(validGatewayTicket), + iamRepoFn: iamRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing auth token repo", + }, + { + name: "missing-servers-repo", + requestCtx: newReqCtx(validGatewayTicket), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + kms: kmsCache, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing servers repo function", + }, + { + name: "missing-kms", + requestCtx: newReqCtx(validGatewayTicket), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing kms", + }, + { + name: "missing-factory-ticket", + requestCtx: newReqCtx(validGatewayTicket), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing ticket", + }, + { + name: "missing-metadata", + requestCtx: context.Background(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "No metadata", + }, + { + name: "missing-request-info-metadata", + requestCtx: func() context.Context { + md := metadata.Pairs("greeter-md", "hello") + return metadata.NewIncomingContext(context.Background(), md) + }(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "Missing request metadata", + }, + { + name: "too-many-request-info-metadata", + requestCtx: func() context.Context { + md := metadata.Pairs(requestInfoMdKey, "first", requestInfoMdKey, "second") + return metadata.NewIncomingContext(context.Background(), md) + }(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "expected 1 value", + }, + { + name: "request-info-metadata-not-encoded", + requestCtx: func() context.Context { + md := metadata.Pairs(requestInfoMdKey, "hello") + return metadata.NewIncomingContext(context.Background(), md) + }(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "unable to decode request info", + }, + { + name: "request-info-metadata-not-proto", + requestCtx: func() context.Context { + md := metadata.Pairs(requestInfoMdKey, base58.FastBase58Encoding([]byte("hello"))) + return metadata.NewIncomingContext(context.Background(), md) + }(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "unable to unmarshal request info", + }, + { + name: "request-info-metadata-not-proto", + requestCtx: func() context.Context { + md := metadata.Pairs(requestInfoMdKey, base58.FastBase58Encoding([]byte("hello"))) + return metadata.NewIncomingContext(context.Background(), md) + }(), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "unable to unmarshal request info", + }, + { + name: "missing-request-ticket", + requestCtx: newReqCtx(""), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: "validGatewayTicket", + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "Invalid context (missing ticket)", + }, + { + name: "bad-ticket", + requestCtx: newReqCtx("bad-ticket"), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: "validGatewayTicket", + wantRequestErr: true, + wantRequestErrMatch: errors.T(errors.Internal), + wantRequestErrContains: "Invalid context (bad ticket)", + }, + { + name: "valid", + requestCtx: newReqCtx(validGatewayTicket), + iamRepoFn: iamRepoFn, + authTokenRepoFn: atRepoFn, + serversRepoFn: serversRepoFn, + kms: kmsCache, + ticket: validGatewayTicket, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + interceptor, err := requestCtxInterceptor(factoryCtx, tt.iamRepoFn, tt.authTokenRepoFn, tt.serversRepoFn, tt.kms, tt.ticket) + if tt.wantFactoryErr { + require.Error(err) + assert.Nil(interceptor) + if tt.wantFactoryErrMatch != nil { + assert.Truef(errors.Match(tt.wantFactoryErrMatch, err), "want err code: %q got: %q", tt.wantFactoryErrMatch.Code, err) + } + if tt.wantFactoryErrContains != "" { + assert.Contains(err.Error(), tt.wantFactoryErrContains) + } + return + } + require.NoError(err) + assert.NotNil(interceptor) + + info := &grpc.UnaryServerInfo{ + FullMethod: "FakeMethod", + } + retCtx, err := interceptor(tt.requestCtx, nil, info, returnCtxHandler) + if tt.wantRequestErr { + require.Error(err) + assert.Nil(retCtx) + if tt.wantRequestErrMatch != nil { + assert.Truef(errors.Match(tt.wantRequestErrMatch, err), "want err code: %q got: %q", tt.wantRequestErrMatch.Code, err) + } + if tt.wantRequestErrContains != "" { + assert.Contains(err.Error(), tt.wantRequestErrContains) + } + return + } + require.NoError(err) + verifyResults := auth.Verify(retCtx.(context.Context)) + assert.NotEmpty(verifyResults) + }) + } +} + +func Test_errorInterceptor(t *testing.T) { + ctx := context.Background() + tests := []struct { + name string + factoryCtx context.Context + wantRequestErr bool + wantDomainErr bool + wantApiErr bool + }{ + { + name: "domain-error", + factoryCtx: ctx, + wantRequestErr: true, + wantDomainErr: true, + }, + { + name: "api-error", + factoryCtx: ctx, + wantRequestErr: true, + wantApiErr: true, + }, + { + name: "success", + factoryCtx: ctx, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + require.Falsef(tt.wantApiErr && tt.wantDomainErr, "tests can't require both wantApiErr and wantDomainErr") + errInterceptor := errorInterceptor(tt.factoryCtx) + client := startTestGreeterService(t, &testGreeter{}, errInterceptor) + var header metadata.MD + resp, err := client.SayHello( + context.Background(), + &interceptor.SayHelloRequest{Name: tt.name}, + grpc.Header(&header), + ) + if tt.wantRequestErr { + require.Error(err) + domainHdr := header.Get(domainErrHeader) + apiErrHdr := header.Get(apiErrHeader) + require.Error(err) + assert.Nil(resp) + if tt.wantDomainErr { + require.Len(domainHdr, 1) + assert.Len(apiErrHdr, 0) + decoded, err := base58.FastBase58Decoding(domainHdr[0]) + require.NoError(err) + var pbErr pberrors.Err + err = proto.Unmarshal(decoded, &pbErr) + require.NoError(err) + } + if tt.wantApiErr { + require.Len(apiErrHdr, 1) + assert.Len(domainHdr, 0) + decoded, err := base58.FastBase58Decoding(apiErrHdr[0]) + require.NoError(err) + var pbErr pberrors.ApiError + err = proto.Unmarshal(decoded, &pbErr) + require.NoError(err) + } + return + } + require.NoError(err) + }) + } +} + +func Test_statusCodeInterceptor(t *testing.T) { + ctx := context.Background() + tests := []struct { + name string + wantStatusCode int + wantErr bool + }{ + { + name: "nil-nil", + wantStatusCode: http.StatusNoContent, + }, + { + name: "nil-err", + wantErr: true, + }, + { + name: "hello", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + statusInterceptor := statusCodeInterceptor(ctx) + client := startTestGreeterService(t, &testGreeter{}, statusInterceptor) + var header metadata.MD + _, err := client.SayHello( + context.Background(), + &interceptor.SayHelloRequest{Name: tt.name}, + grpc.Header(&header), + ) + + if tt.wantErr { + assert.Error(err) + } + statusHdr := header.Get(handlers.StatusCodeHeader) + if tt.wantStatusCode > 0 { + require.Len(statusHdr, 1) + code, err := strconv.Atoi(statusHdr[0]) + require.NoError(err) + assert.Equal(tt.wantStatusCode, code) + } else { + require.Len(statusHdr, 0) + } + }) + } +} + +type testGreeter struct { + interceptor.UnimplementedGreeterServiceServer +} + +func (g *testGreeter) SayHello(ctx context.Context, req *interceptor.SayHelloRequest) (*interceptor.SayHelloResponse, error) { + const op = "SayHello" + switch req.GetName() { + case "api-error": + return &interceptor.SayHelloResponse{}, &handlers.ApiError{ + Status: http.StatusInternalServerError, + Inner: &pb_api.Error{ + Kind: "internal", + Op: op, + Message: "api error msg", + }, + } + case "domain-error": + return &interceptor.SayHelloResponse{Message: "hello"}, errors.New(ctx, errors.Internal, op, "domain error msg") + case "nil-nil": + return nil, nil + case "nil-err": + return nil, errors.New(ctx, errors.Internal, op, "nil response error msg") + default: + return &interceptor.SayHelloResponse{Message: "hello"}, nil + } +} diff --git a/internal/servers/controller/listeners.go b/internal/servers/controller/listeners.go index 9a0d2feae..ad62e2822 100644 --- a/internal/servers/controller/listeners.go +++ b/internal/servers/controller/listeners.go @@ -12,6 +12,7 @@ import ( "sync" "time" + "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" "github.com/hashicorp/boundary/internal/libs/alpnmux" @@ -20,10 +21,16 @@ import ( "google.golang.org/grpc" ) -func (c *Controller) startListeners() error { +func (c *Controller) startListeners(ctx context.Context) error { servers := make([]func(), 0, len(c.conf.Listeners)) configureForAPI := func(ln *base.ServerListener) error { + var err error + if c.gatewayServer, c.gatewayTicket, err = newGatewayServer(ctx, c.IamRepoFn, c.AuthTokenRepoFn, c.ServersRepoFn, c.kms); err != nil { + return err + } + c.gatewayMux = newGatewayMux() + handler, err := c.handler(HandlerProperties{ ListenerConfig: ln.Config, CancelCtx: c.baseContext, @@ -134,6 +141,11 @@ func (c *Controller) startListeners() error { return nil } + c.gatewayListener, _ = newGatewayListener() + servers = append(servers, func() { + go c.gatewayServer.Serve(c.gatewayListener) + }) + for _, ln := range c.conf.Listeners { var err error for _, purpose := range ln.Config.Purpose { @@ -184,6 +196,21 @@ func (c *Controller) stopListeners(serversOnly bool) error { } }() } + + if c.gatewayServer != nil { + serverWg.Add(1) + go func() { + defer serverWg.Done() + shutdownKill, shutdownKillCancel := context.WithTimeout(c.baseContext, globals.DefaultMaxRequestDuration) + defer shutdownKillCancel() + go func() { + <-shutdownKill.Done() + c.gatewayServer.Stop() + }() + c.gatewayServer.GracefulStop() + }() + } + serverWg.Wait() if serversOnly { return nil diff --git a/internal/servers/controller/testing.go b/internal/servers/controller/testing.go index 247c6bc97..11691ad98 100644 --- a/internal/servers/controller/testing.go +++ b/internal/servers/controller/testing.go @@ -10,6 +10,7 @@ import ( "testing" "time" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/hashicorp/boundary/api" "github.com/hashicorp/boundary/api/authmethods" "github.com/hashicorp/boundary/api/authtokens" @@ -18,6 +19,7 @@ import ( "github.com/hashicorp/boundary/internal/cmd/config" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/schema" + "github.com/hashicorp/boundary/internal/gen/testing/interceptor" "github.com/hashicorp/boundary/internal/host/plugin" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/intglobals" @@ -28,6 +30,9 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-secure-stdlib/strutil" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/test/bufconn" ) const ( @@ -718,3 +723,39 @@ func (tc *TestController) WaitForNextWorkerStatusUpdate(workerId string) error { event.WriteSysEvent(ctx, op, "waiting for next status report from worker received successfully", "worker", workerId) return nil } + +// startTestGreeterService is intended to facilitate the testing of +// interceptors. You provide a greeter service that produces appropriate +// responses to test your interceptor(s). This test function will start up a +// test greeter service wrapped with the specified interceptors and return an +// initialized client for the service. The test service will be stopped/cleaned +// up after the test (or subtests) have completed. +func startTestGreeterService(t *testing.T, greeter interceptor.GreeterServiceServer, interceptors ...grpc.UnaryServerInterceptor) interceptor.GreeterServiceClient { + t.Helper() + require := require.New(t) + dialCtx := context.Background() + + buffer := 1024 * 1024 + listener := bufconn.Listen(buffer) + + s := grpc.NewServer( + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(interceptors...)), + ) + interceptor.RegisterGreeterServiceServer(s, greeter) + go func() { + err := s.Serve(listener) + require.NoError(err) + }() + + conn, _ := grpc.DialContext(dialCtx, "", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + }), grpc.WithInsecure()) + + t.Cleanup(func() { + listener.Close() + s.Stop() + }) + + client := interceptor.NewGreeterServiceClient(conn) + return client +} diff --git a/internal/servers/query.go b/internal/servers/query.go index 1e814a853..c0f865a17 100644 --- a/internal/servers/query.go +++ b/internal/servers/query.go @@ -1,18 +1,6 @@ package servers const ( - serverUpsertQuery = ` - insert into server - (private_id, type, description, address, update_time) - values - (@private_id, @type, @description, @address, now()) - on conflict on constraint server_pkey - do update set - type = @type, - description = @description, - address = @address, - update_time = now(); - ` deleteWhereCreateTimeSql = `create_time < ?` deleteTagsSql = `server_id = ?` ) diff --git a/internal/servers/repository.go b/internal/servers/repository.go index 2634e87a7..1a5a49639 100644 --- a/internal/servers/repository.go +++ b/internal/servers/repository.go @@ -2,7 +2,6 @@ package servers import ( "context" - "database/sql" "fmt" "time" @@ -138,7 +137,7 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op opts := getOpts(opt...) - var rowsUpdated int + var rowsUpdated int64 var controllers []*Server _, err := r.writer.DoTx( ctx, @@ -146,14 +145,11 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op db.ExpBackoff{}, func(read db.Reader, w db.Writer) error { var err error - rowsUpdated, err = w.Exec(ctx, - serverUpsertQuery, - []interface{}{ - sql.Named("private_id", server.PrivateId), - sql.Named("type", server.Type), - sql.Named("description", server.Description), - sql.Named("address", server.Address), - }) + onConflict := &db.OnConflict{ + Target: db.Constraint("server_pkey"), + Action: append(db.SetColumns([]string{"type", "description", "address"}), db.SetColumnValues(map[string]interface{}{"update_time": "now()"})...), + } + err = w.Create(ctx, server, db.WithOnConflict(onConflict), db.WithReturnRowsAffected(&rowsUpdated)) if err != nil { return errors.Wrap(ctx, err, op+":Upsert") } @@ -208,7 +204,7 @@ func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Op return nil, db.NoRowsAffected, err } - return controllers, rowsUpdated, nil + return controllers, int(rowsUpdated), nil } type RecoveryNonce struct { diff --git a/internal/servers/servers.pb.go b/internal/servers/servers.pb.go index b5fad0c29..cd07d8563 100644 --- a/internal/servers/servers.pb.go +++ b/internal/servers/servers.pb.go @@ -35,9 +35,10 @@ type Server struct { // private ID and name to be identical, and there is currently no reason we // can think of to allow a given server's name to match another within the // same cluster. So we simply use the private ID and deprecate this. + // @inject_tag: `gorm:"-"` // // Deprecated: Do not use. - Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty"` + Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"-"` // Description of the resource Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty"` // Address for the server diff --git a/internal/session/repository_session_test.go b/internal/session/repository_session_test.go index 890e5b260..68e9076f1 100644 --- a/internal/session/repository_session_test.go +++ b/internal/session/repository_session_test.go @@ -15,7 +15,8 @@ import ( "github.com/hashicorp/boundary/internal/host/static" staticStore "github.com/hashicorp/boundary/internal/host/static/store" "github.com/hashicorp/boundary/internal/target" - targetStore "github.com/hashicorp/boundary/internal/target/store" + "github.com/hashicorp/boundary/internal/target/tcp" + tcpStore "github.com/hashicorp/boundary/internal/target/tcp/store" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/jackc/pgconn" "google.golang.org/protobuf/types/known/timestamppb" @@ -1314,8 +1315,8 @@ func TestRepository_CancelSessionViaFKNull(t *testing.T) { cancelFk: func() cancelFk { s := setupFn() - t := &target.TcpTarget{ - TcpTarget: &targetStore.TcpTarget{ + t := &tcp.Target{ + Target: &tcpStore.Target{ PublicId: s.TargetId, }, } diff --git a/internal/session/testing.go b/internal/session/testing.go index 652f5ac98..7a96e2778 100644 --- a/internal/session/testing.go +++ b/internal/session/testing.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/servers" "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-secure-stdlib/base62" "github.com/hashicorp/go-uuid" @@ -132,7 +133,7 @@ func TestSessionParams(t *testing.T, conn *db.DB, wrapper wrapping.Wrapper, iamR sets := static.TestSets(t, conn, cats[0].PublicId, 1) _ = static.TestSetMembers(t, conn, sets[0].PublicId, hosts) - tcpTarget := target.TestTcpTarget(t, conn, proj.PublicId, "test target") + tcpTarget := tcp.TestTarget(t, conn, proj.PublicId, "test target") kms := kms.TestKms(t, conn, wrapper) targetRepo, err := target.NewRepository(rw, rw, kms) diff --git a/internal/target/credential_library_test.go b/internal/target/credential_library_test.go index 38a86a4b9..a079f471b 100644 --- a/internal/target/credential_library_test.go +++ b/internal/target/credential_library_test.go @@ -1,10 +1,11 @@ -package target +package target_test import ( "testing" "github.com/hashicorp/boundary/internal/credential" "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/target" "github.com/hashicorp/boundary/internal/target/store" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,7 +19,7 @@ func TestCredentialLibrary_New(t *testing.T) { tests := []struct { name string args args - want *CredentialLibrary + want *target.CredentialLibrary wantErr errors.Code }{ { @@ -41,7 +42,7 @@ func TestCredentialLibrary_New(t *testing.T) { targetId: "targ_0000000", libraryId: "lib_0000000", }, - want: &CredentialLibrary{ + want: &target.CredentialLibrary{ CredentialLibrary: &store.CredentialLibrary{ TargetId: "targ_0000000", CredentialLibraryId: "lib_0000000", @@ -54,7 +55,7 @@ func TestCredentialLibrary_New(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - got, err := NewCredentialLibrary(tt.args.targetId, tt.args.libraryId) + got, err := target.NewCredentialLibrary(tt.args.targetId, tt.args.libraryId) if tt.wantErr != 0 { assert.Truef(errors.Match(errors.T(tt.wantErr), err), "want err: %q got: %q", tt.wantErr, err) assert.Nil(got) diff --git a/internal/target/exports_test.go b/internal/target/exports_test.go new file mode 100644 index 000000000..cada8aa31 --- /dev/null +++ b/internal/target/exports_test.go @@ -0,0 +1,7 @@ +package target + +// Expose functions for use in tests. +var ( + AllocTargetView = allocTargetView + TargetsViewDefaultTable = targetsViewDefaultTable +) diff --git a/internal/target/host_set.go b/internal/target/host_set.go index 7ccee9ed5..8326dadda 100644 --- a/internal/target/host_set.go +++ b/internal/target/host_set.go @@ -44,13 +44,6 @@ func NewTargetHostSet(targetId, hostSetId string, _ ...Option) (*TargetHostSet, return t, nil } -// allocTargetHostSet will allocate a target host set -func allocTargetHostSet() TargetHostSet { - return TargetHostSet{ - TargetHostSet: &store.TargetHostSet{}, - } -} - // Clone creates a clone of the target host set func (t *TargetHostSet) Clone() interface{} { cp := proto.Clone(t.TargetHostSet) diff --git a/internal/target/ids.go b/internal/target/ids.go deleted file mode 100644 index 618572621..000000000 --- a/internal/target/ids.go +++ /dev/null @@ -1,19 +0,0 @@ -package target - -import ( - "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/errors" -) - -const ( - TcpTargetPrefix = "ttcp" -) - -func newTcpTargetId() (string, error) { - const op = "target.newTcpTargetId" - id, err := db.NewPublicId(TcpTargetPrefix) - if err != nil { - return "", errors.WrapDeprecated(err, op) - } - return id, nil -} diff --git a/internal/target/ids_test.go b/internal/target/ids_test.go deleted file mode 100644 index eac126b1a..000000000 --- a/internal/target/ids_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package target - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Ids(t *testing.T) { - t.Parallel() - t.Run("tcp", func(t *testing.T) { - id, err := newTcpTargetId() - require.NoError(t, err) - assert.True(t, strings.HasPrefix(id, TcpTargetPrefix+"_")) - }) -} diff --git a/internal/target/options.go b/internal/target/options.go index 1f39fb47b..5fa1bbe08 100644 --- a/internal/target/options.go +++ b/internal/target/options.go @@ -1,9 +1,13 @@ package target -import "time" +import ( + "time" -// getOpts - iterate the inbound Options and return a struct -func getOpts(opt ...Option) options { + "github.com/hashicorp/boundary/internal/types/subtypes" +) + +// GetOpts - iterate the inbound Options and return a struct +func GetOpts(opt ...Option) options { opts := getDefaultOptions() for _, o := range opt { o(&opts) @@ -16,54 +20,54 @@ type Option func(*options) // options = how options are represented type options struct { - withName string - withDescription string - withDefaultPort uint32 - withLimit int - withScopeId string - withScopeIds []string - withScopeName string - withUserId string - withTargetType *TargetType - withHostSources []string - withCredentialSources []string - withSessionMaxSeconds uint32 - withSessionConnectionLimit int32 - withPublicId string - withWorkerFilter string + WithName string + WithDescription string + WithDefaultPort uint32 + WithLimit int + WithScopeId string + WithScopeIds []string + WithScopeName string + WithUserId string + WithType subtypes.Subtype + WithHostSources []string + WithCredentialSources []string + WithSessionMaxSeconds uint32 + WithSessionConnectionLimit int32 + WithPublicId string + WithWorkerFilter string } func getDefaultOptions() options { return options{ - withName: "", - withDescription: "", - withLimit: 0, - withDefaultPort: 0, - withScopeId: "", - withScopeIds: nil, - withScopeName: "", - withUserId: "", - withTargetType: nil, - withHostSources: nil, - withCredentialSources: nil, - withSessionMaxSeconds: uint32((8 * time.Hour).Seconds()), - withSessionConnectionLimit: 1, - withPublicId: "", - withWorkerFilter: "", + WithName: "", + WithDescription: "", + WithLimit: 0, + WithDefaultPort: 0, + WithScopeId: "", + WithScopeIds: nil, + WithScopeName: "", + WithUserId: "", + WithType: "", + WithHostSources: nil, + WithCredentialSources: nil, + WithSessionMaxSeconds: uint32((8 * time.Hour).Seconds()), + WithSessionConnectionLimit: 1, + WithPublicId: "", + WithWorkerFilter: "", } } // WithDescription provides an optional description func WithDescription(desc string) Option { return func(o *options) { - o.withDescription = desc + o.WithDescription = desc } } // WithName provides an option to search by a friendly name func WithName(name string) Option { return func(o *options) { - o.withName = name + o.WithName = name } } @@ -72,88 +76,88 @@ func WithName(name string) Option { // If WithLimit == 0, then default limits are used for results. func WithLimit(limit int) Option { return func(o *options) { - o.withLimit = limit + o.WithLimit = limit } } // WithDefaultPort provides an option to specify the default target port. func WithDefaultPort(p uint32) Option { return func(o *options) { - o.withDefaultPort = p + o.WithDefaultPort = p } } // WithScopeId provides an option to search by a scope id func WithScopeId(scopeId string) Option { return func(o *options) { - o.withScopeId = scopeId + o.WithScopeId = scopeId } } // WithScopeId provides an option to search by multiple scope id func WithScopeIds(scopeIds []string) Option { return func(o *options) { - o.withScopeIds = scopeIds + o.WithScopeIds = scopeIds } } // WithScopeId provides an option to search by a scope name func WithScopeName(scopeName string) Option { return func(o *options) { - o.withScopeName = scopeName + o.WithScopeName = scopeName } } // WithUserId provides an option to search by a user public id func WithUserId(userId string) Option { return func(o *options) { - o.withUserId = userId + o.WithUserId = userId } } -// WithTargetType provides an option to search by a target type -func WithTargetType(t TargetType) Option { +// WithType provides an option to search by a target type +func WithType(t subtypes.Subtype) Option { return func(o *options) { - o.withTargetType = &t + o.WithType = t } } // WithHostSources provides an option for providing a list of host source ids func WithHostSources(hs []string) Option { return func(o *options) { - o.withHostSources = hs + o.WithHostSources = hs } } // WithCredentialSources provides an option for providing a list of credential source ids func WithCredentialSources(cl []string) Option { return func(o *options) { - o.withCredentialSources = cl + o.WithCredentialSources = cl } } func WithSessionMaxSeconds(dur uint32) Option { return func(o *options) { - o.withSessionMaxSeconds = dur + o.WithSessionMaxSeconds = dur } } func WithSessionConnectionLimit(limit int32) Option { return func(o *options) { - o.withSessionConnectionLimit = limit + o.WithSessionConnectionLimit = limit } } // WithPublicId provides an optional public id func WithPublicId(id string) Option { return func(o *options) { - o.withPublicId = id + o.WithPublicId = id } } // WithWorkerFilter provides an optional worker filter func WithWorkerFilter(filter string) Option { return func(o *options) { - o.withWorkerFilter = filter + o.WithWorkerFilter = filter } } diff --git a/internal/target/options_test.go b/internal/target/options_test.go index 298cb373c..a4d3431f5 100644 --- a/internal/target/options_test.go +++ b/internal/target/options_test.go @@ -3,6 +3,7 @@ package target import ( "testing" + "github.com/hashicorp/boundary/internal/types/subtypes" "github.com/stretchr/testify/assert" ) @@ -11,104 +12,105 @@ func Test_GetOpts(t *testing.T) { t.Parallel() t.Run("WithName", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithName("test")) + opts := GetOpts(WithName("test")) testOpts := getDefaultOptions() - testOpts.withName = "test" + testOpts.WithName = "test" assert.Equal(opts, testOpts) }) t.Run("WithDescription", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithDescription("test desc")) + opts := GetOpts(WithDescription("test desc")) testOpts := getDefaultOptions() - testOpts.withDescription = "test desc" + testOpts.WithDescription = "test desc" assert.Equal(opts, testOpts) }) t.Run("WithLimit", func(t *testing.T) { assert := assert.New(t) // test default of 0 - opts := getOpts() + opts := GetOpts() testOpts := getDefaultOptions() - testOpts.withLimit = 0 + testOpts.WithLimit = 0 assert.Equal(opts, testOpts) - opts = getOpts(WithLimit(-1)) + opts = GetOpts(WithLimit(-1)) testOpts = getDefaultOptions() - testOpts.withLimit = -1 + testOpts.WithLimit = -1 assert.Equal(opts, testOpts) - opts = getOpts(WithLimit(1)) + opts = GetOpts(WithLimit(1)) testOpts = getDefaultOptions() - testOpts.withLimit = 1 + testOpts.WithLimit = 1 assert.Equal(opts, testOpts) }) t.Run("WithDefaultPort", func(t *testing.T) { assert := assert.New(t) // test default of 0 - opts := getOpts() + opts := GetOpts() testOpts := getDefaultOptions() - testOpts.withDefaultPort = 0 + testOpts.WithDefaultPort = 0 assert.Equal(opts, testOpts) - opts = getOpts(WithDefaultPort(22)) + opts = GetOpts(WithDefaultPort(22)) testOpts = getDefaultOptions() - testOpts.withDefaultPort = uint32(22) + testOpts.WithDefaultPort = uint32(22) assert.Equal(opts, testOpts) }) t.Run("WithUserId", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithUserId("testId")) + opts := GetOpts(WithUserId("testId")) testOpts := getDefaultOptions() - testOpts.withUserId = "testId" + testOpts.WithUserId = "testId" assert.Equal(opts, testOpts) }) t.Run("WithScopeId", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithScopeId("testId")) + opts := GetOpts(WithScopeId("testId")) testOpts := getDefaultOptions() - testOpts.withScopeId = "testId" + testOpts.WithScopeId = "testId" assert.Equal(opts, testOpts) }) t.Run("WithScopeName", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithScopeName("testName")) + opts := GetOpts(WithScopeName("testName")) testOpts := getDefaultOptions() - testOpts.withScopeName = "testName" + testOpts.WithScopeName = "testName" assert.Equal(opts, testOpts) }) t.Run("WithPublicId", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithPublicId("testId")) + opts := GetOpts(WithPublicId("testId")) testOpts := getDefaultOptions() - testOpts.withPublicId = "testId" + testOpts.WithPublicId = "testId" assert.Equal(opts, testOpts) }) - t.Run("WithTargetType", func(t *testing.T) { + t.Run("WithType", func(t *testing.T) { + subtype := subtypes.Subtype("testtype") assert := assert.New(t) - opts := getOpts(WithTargetType(TcpTargetType)) + opts := GetOpts(WithType(subtype)) testOpts := getDefaultOptions() - target := TcpTargetType - testOpts.withTargetType = &target + target := subtype + testOpts.WithType = target assert.Equal(opts, testOpts) }) t.Run("WithHostSources", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithHostSources([]string{"alice", "bob"})) + opts := GetOpts(WithHostSources([]string{"alice", "bob"})) testOpts := getDefaultOptions() - testOpts.withHostSources = []string{"alice", "bob"} + testOpts.WithHostSources = []string{"alice", "bob"} assert.Equal(opts, testOpts) }) t.Run("WithWorkerFilter", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithWorkerFilter(`"/foo" == "bar"`)) + opts := GetOpts(WithWorkerFilter(`"/foo" == "bar"`)) testOpts := getDefaultOptions() - testOpts.withWorkerFilter = `"/foo" == "bar"` + testOpts.WithWorkerFilter = `"/foo" == "bar"` assert.Equal(opts, testOpts) }) t.Run("WithCredentialSources", func(t *testing.T) { assert := assert.New(t) - opts := getOpts(WithCredentialSources([]string{"alice", "bob"})) + opts := GetOpts(WithCredentialSources([]string{"alice", "bob"})) testOpts := getDefaultOptions() - testOpts.withCredentialSources = []string{"alice", "bob"} + testOpts.WithCredentialSources = []string{"alice", "bob"} assert.Equal(opts, testOpts) }) } diff --git a/internal/target/registry.go b/internal/target/registry.go new file mode 100644 index 000000000..d33f3035c --- /dev/null +++ b/internal/target/registry.go @@ -0,0 +1,112 @@ +package target + +import ( + "context" + "fmt" + "sync" + + "github.com/hashicorp/boundary/internal/types/subtypes" +) + +// AllocFunc is a function that creates an in-memory Target. +type AllocFunc func() Target + +// VetFunc is a function that checks the given Target to ensure it can +// be used by the Repository. +type VetFunc func(context.Context, Target) error + +type registryEntry struct { + alloc AllocFunc + vet VetFunc + prefix string +} + +type registry struct { + m map[subtypes.Subtype]*registryEntry + subtypes *subtypes.Registry + + sync.RWMutex +} + +func (r *registry) set(s subtypes.Subtype, entry *registryEntry) { + r.Lock() + defer r.Unlock() + + _, previouslySet := r.m[s] + if previouslySet { + panic(fmt.Sprintf("target subtype %s already registered", s)) + } + + if err := r.subtypes.Register(s, entry.prefix); err != nil { + panic(err) + } + + r.m[s] = entry +} + +func (r *registry) get(s subtypes.Subtype) (*registryEntry, bool) { + r.RLock() + defer r.RUnlock() + + entry, ok := r.m[s] + if ok { + return entry, ok + } + return nil, ok +} + +func (r *registry) allocFunc(s subtypes.Subtype) (AllocFunc, bool) { + entry, ok := r.get(s) + if !ok { + return nil, ok + } + return entry.alloc, ok +} + +func (r *registry) vetFunc(s subtypes.Subtype) (VetFunc, bool) { + entry, ok := r.get(s) + if !ok { + return nil, ok + } + return entry.vet, ok +} + +func (r *registry) idPrefix(s subtypes.Subtype) (string, bool) { + entry, ok := r.get(s) + if !ok { + return "", ok + } + return entry.prefix, ok +} + +var subtypeRegistry = registry{ + m: make(map[subtypes.Subtype]*registryEntry), + subtypes: subtypes.NewRegistry(), +} + +// SubtypeFromType returns the Subtype from the provided string or if +// no Subtype was registered with that string Unknown is returned. +func SubtypeFromType(t string) subtypes.Subtype { + subtypeRegistry.RLock() + defer subtypeRegistry.RUnlock() + return subtypeRegistry.subtypes.SubtypeFromType(t) +} + +// SubtypeFromId returns the Subtype from the provided id if the id's prefix +// was registered with a Subtype. Otherwise Unknown is returned. +func SubtypeFromId(id string) subtypes.Subtype { + subtypeRegistry.RLock() + defer subtypeRegistry.RUnlock() + return subtypeRegistry.subtypes.SubtypeFromId(id) +} + +// Register registers repository hooks and the prefixes for a provided Subtype. Register +// panics if the subtype has already been registered or if any of the +// prefixes are associated with another subtype. +func Register(s subtypes.Subtype, af AllocFunc, vf VetFunc, prefix string) { + subtypeRegistry.set(s, ®istryEntry{ + alloc: af, + vet: vf, + prefix: prefix, + }) +} diff --git a/internal/target/repository.go b/internal/target/repository.go index dff2cbebd..04843199c 100644 --- a/internal/target/repository.go +++ b/internal/target/repository.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/boundary/internal/db" + dbcommon "github.com/hashicorp/boundary/internal/db/common" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" @@ -13,7 +14,7 @@ import ( // Cloneable provides a cloning interface type Cloneable interface { - Clone() interface{} + Clone() Target } // Repository is the target database repository @@ -39,16 +40,16 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo if kms == nil { return nil, errors.NewDeprecated(errors.InvalidParameter, op, "nil kms") } - opts := getOpts(opt...) - if opts.withLimit == 0 { + opts := GetOpts(opt...) + if opts.WithLimit == 0 { // zero signals the boundary defaults should be used. - opts.withLimit = db.DefaultLimit + opts.WithLimit = db.DefaultLimit } return &Repository{ reader: r, writer: w, kms: kms, - defaultLimit: opts.withLimit, + defaultLimit: opts.WithLimit, }, nil } @@ -57,7 +58,7 @@ func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms, opt ...Option) (*Repo // found, it will return nil, nil, nil, nil. No options are currently supported. func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, opt ...Option) (Target, []HostSource, []CredentialSource, error) { const op = "target.(Repository).LookupTarget" - opts := getOpts(opt...) + opts := GetOpts(opt...) if publicIdOrName == "" { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing public id") @@ -65,23 +66,23 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op var where []string var whereArgs []interface{} - nameEmpty := opts.withName == "" - scopeIdEmpty := opts.withScopeId == "" - scopeNameEmpty := opts.withScopeName == "" + nameEmpty := opts.WithName == "" + scopeIdEmpty := opts.WithScopeId == "" + scopeNameEmpty := opts.WithScopeName == "" if !nameEmpty { - if opts.withName != publicIdOrName { + if opts.WithName != publicIdOrName { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "name passed in but does not match publicId") } - where, whereArgs = append(where, "lower(name) = lower(?)"), append(whereArgs, opts.withName) + where, whereArgs = append(where, "lower(name) = lower(?)"), append(whereArgs, opts.WithName) switch { case scopeIdEmpty && scopeNameEmpty: return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "using name but both scope ID and scope name are empty") case !scopeIdEmpty && !scopeNameEmpty: return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "using name but both scope ID and scope name are set") case !scopeIdEmpty: - where, whereArgs = append(where, "scope_id = ?"), append(whereArgs, opts.withScopeId) + where, whereArgs = append(where, "scope_id = ?"), append(whereArgs, opts.WithScopeId) case !scopeNameEmpty: - where, whereArgs = append(where, "scope_id = (select public_id from iam_scope where lower(name) = lower(?))"), append(whereArgs, opts.withScopeName) + where, whereArgs = append(where, "scope_id = (select public_id from iam_scope where lower(name) = lower(?))"), append(whereArgs, opts.WithScopeName) default: return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "unknown combination of parameters") } @@ -130,28 +131,28 @@ func (r *Repository) LookupTarget(ctx context.Context, publicIdOrName string, op } return nil, nil, nil, errors.Wrap(ctx, err, op) } - subtype, err := target.targetSubtype() + subtype, err := target.targetSubtype(ctx) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op) } return subtype, hostSources, credSources, nil } -// ListTargets in targets in a scope. Supports the WithScopeId, WithLimit, WithTargetType options. +// ListTargets in targets in a scope. Supports the WithScopeId, WithLimit, WithType options. func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, error) { const op = "target.(Repository).ListTargets" - opts := getOpts(opt...) - if len(opts.withScopeIds) == 0 && opts.withUserId == "" { + opts := GetOpts(opt...) + if len(opts.WithScopeIds) == 0 && opts.WithUserId == "" { return nil, errors.New(ctx, errors.InvalidParameter, op, "must specify either scope id or user id") } // TODO (jimlambrt 8/2020) - implement WithUserId() optional filtering. var where []string var args []interface{} - if len(opts.withScopeIds) != 0 { - where, args = append(where, "scope_id in (?)"), append(args, opts.withScopeIds) + if len(opts.WithScopeIds) != 0 { + where, args = append(where, "scope_id in (?)"), append(args, opts.WithScopeIds) } - if opts.withTargetType != nil { - where, args = append(where, "type = ?"), append(args, opts.withTargetType.String()) + if opts.WithType != "" { + where, args = append(where, "type = ?"), append(args, opts.WithType.String()) } var foundTargets []*targetView @@ -163,7 +164,7 @@ func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, targets := make([]Target, 0, len(foundTargets)) for _, t := range foundTargets { - subtype, err := t.targetSubtype() + subtype, err := t.targetSubtype(ctx) if err != nil { return nil, errors.Wrap(ctx, err, op) } @@ -176,12 +177,12 @@ func (r *Repository) ListTargets(ctx context.Context, opt ...Option) ([]Target, // repo defaultLimit func (r *Repository) list(ctx context.Context, resources interface{}, where string, args []interface{}, opt ...Option) error { const op = "target.(Repository).list" - opts := getOpts(opt...) + opts := GetOpts(opt...) limit := r.defaultLimit var dbOpts []db.Option - if opts.withLimit != 0 { + if opts.WithLimit != 0 { // non-zero signals an override of the default limit for the repo. - limit = opts.withLimit + limit = opts.WithLimit } dbOpts = append(dbOpts, db.WithLimit(limit)) if err := r.reader.SearchWhere(ctx, resources, where, args, dbOpts...); err != nil { @@ -202,17 +203,16 @@ func (r *Repository) DeleteTarget(ctx context.Context, publicId string, _ ...Opt return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", publicId))) } var metadata oplog.Metadata - var deleteTarget interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = publicId - deleteTarget = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_DELETE) - default: + var deleteTarget Target + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", publicId, t.Type)) } + deleteTarget = alloc() + deleteTarget.SetPublicId(ctx, publicId) + metadata = deleteTarget.Oplog(oplog.OpType_OP_TYPE_DELETE) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.ScopeId, kms.KeyPurposeOplog) if err != nil { return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) @@ -277,7 +277,7 @@ func (r *Repository) update(ctx context.Context, target Target, version uint32, if err != nil { return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) } - metadata := target.oplog(oplog.OpType_OP_TYPE_UPDATE) + metadata := target.Oplog(oplog.OpType_OP_TYPE_UPDATE) dbOpts = append(dbOpts, db.WithOplog(oplogWrapper, metadata)) var rowsUpdated int @@ -320,3 +320,202 @@ func (r *Repository) update(ctx context.Context, target Target, version uint32, } return returnedTarget.(Target), hostSources, credSources, rowsUpdated, nil } + +// CreateTarget inserts into the repository and returns the new Target with +// its list of host sets and credential libraries. +// WithHostSources and WithCredentialSources are the only supported option. +func (r *Repository) CreateTarget(ctx context.Context, target Target, opt ...Option) (Target, []HostSource, []CredentialSource, error) { + const op = "target.(Repository).CreateTarget" + opts := GetOpts(opt...) + if target == nil { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing target") + } + + vet, ok := subtypeRegistry.vetFunc(target.GetType()) + if !ok { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported target type %s", target.GetType())) + } + if err := vet(ctx, target); err != nil { + return nil, nil, nil, err + } + if target.GetScopeId() == "" { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if target.GetName() == "" { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing name") + } + if target.GetPublicId() != "" { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") + } + + t := target.Clone() + + if opts.WithPublicId != "" { + if err := t.SetPublicId(ctx, opts.WithPublicId); err != nil { + return nil, nil, nil, err + } + } else { + prefix, ok := subtypeRegistry.idPrefix(target.GetType()) + if !ok { + return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported target type %s", target.GetType())) + } + id, err := db.NewPublicId(prefix) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op) + } + t.SetPublicId(ctx, id) + } + + oplogWrapper, err := r.kms.GetWrapper(ctx, target.GetScopeId(), kms.KeyPurposeOplog) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) + } + + newHostSets := make([]interface{}, 0, len(opts.WithHostSources)) + for _, hsId := range opts.WithHostSources { + hostSet, err := NewTargetHostSet(t.GetPublicId(), hsId) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory target host set")) + } + newHostSets = append(newHostSets, hostSet) + } + + newCredLibs := make([]interface{}, 0, len(opts.WithCredentialSources)) + for _, clId := range opts.WithCredentialSources { + credLib, err := NewCredentialLibrary(t.GetPublicId(), clId) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory target credential library")) + } + newCredLibs = append(newCredLibs, credLib) + } + + metadata := t.Oplog(oplog.OpType_OP_TYPE_CREATE) + var returnedTarget interface{} + var returnedHostSources []HostSource + var returnedCredSources []CredentialSource + _, err = r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + targetTicket, err := w.GetTicket(t) + if err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) + } + msgs := make([]*oplog.Message, 0, 2) + var targetOplogMsg oplog.Message + returnedTarget = t.Clone() + if err := w.Create(ctx, returnedTarget, db.NewOplogMsg(&targetOplogMsg)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target")) + } + msgs = append(msgs, &targetOplogMsg) + if len(newHostSets) > 0 { + hostSetOplogMsgs := make([]*oplog.Message, 0, len(newHostSets)) + if err := w.CreateItems(ctx, newHostSets, db.NewOplogMsgs(&hostSetOplogMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add host sets")) + } + if returnedHostSources, err = fetchHostSources(ctx, read, t.GetPublicId()); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to read host sources")) + } + msgs = append(msgs, hostSetOplogMsgs...) + } + if len(newCredLibs) > 0 { + credLibOplogMsgs := make([]*oplog.Message, 0, len(newCredLibs)) + if err := w.CreateItems(ctx, newCredLibs, db.NewOplogMsgs(&credLibOplogMsgs)); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add credential sources")) + } + if returnedCredSources, err = fetchCredentialSources(ctx, read, t.GetPublicId()); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to read credential sources")) + } + msgs = append(msgs, credLibOplogMsgs...) + } + if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { + return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) + } + + return nil + }, + ) + if err != nil { + return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s target id", t.GetPublicId()))) + } + return returnedTarget.(Target), returnedHostSources, returnedCredSources, nil +} + +// UpdateTarget will update a target in the repository and return the written +// target. fieldMaskPaths provides field_mask.proto paths for fields that should +// be updated. Fields will be set to NULL if the field is a zero value and +// included in fieldMask. Name, Description, and WorkerFilter are the only +// updatable fields. If no updatable fields are included in the fieldMaskPaths, +// then an error is returned. +func (r *Repository) UpdateTarget(ctx context.Context, target Target, version uint32, fieldMaskPaths []string, _ ...Option) (Target, []HostSource, []CredentialSource, int, error) { + const op = "target.(Repository).UpdateTarget" + if target == nil { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target") + } + vet, ok := subtypeRegistry.vetFunc(target.GetType()) + if !ok { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("unsupported target type %s", target.GetType())) + } + if err := vet(ctx, target); err != nil { + return nil, nil, nil, db.NoRowsAffected, err + } + + if target.GetPublicId() == "" { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target public id") + } + + for _, f := range fieldMaskPaths { + switch { + case strings.EqualFold("name", f): + case strings.EqualFold("description", f): + case strings.EqualFold("defaultport", f): + case strings.EqualFold("sessionmaxseconds", f): + case strings.EqualFold("sessionconnectionlimit", f): + case strings.EqualFold("workerfilter", f): + default: + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) + } + } + var dbMask, nullFields []string + dbMask, nullFields = dbcommon.BuildUpdatePaths( + map[string]interface{}{ + "Name": target.GetName(), + "Description": target.GetDescription(), + "DefaultPort": target.GetDefaultPort(), + "SessionMaxSeconds": target.GetSessionMaxSeconds(), + "SessionConnectionLimit": target.GetSessionConnectionLimit(), + "WorkerFilter": target.GetWorkerFilter(), + }, + fieldMaskPaths, + []string{"SessionMaxSeconds", "SessionConnectionLimit"}, + ) + if len(dbMask) == 0 && len(nullFields) == 0 { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "empty field mask") + } + var returnedTarget Target + var rowsUpdated int + var hostSources []HostSource + var credSources []CredentialSource + _, err := r.writer.DoTx( + ctx, + db.StdRetryCnt, + db.ExpBackoff{}, + func(read db.Reader, w db.Writer) error { + var err error + t := target.Clone() + returnedTarget, hostSources, credSources, rowsUpdated, err = r.update(ctx, t, version, dbMask, nullFields) + if err != nil { + return errors.Wrap(ctx, err, op) + } + return nil + }, + ) + if err != nil { + if errors.IsUniqueError(err) { + return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("target %s already exists in scope %s", target.GetName(), target.GetScopeId())) + } + return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", target.GetPublicId()))) + } + return returnedTarget, hostSources, credSources, rowsUpdated, nil +} diff --git a/internal/target/repository_credential_source.go b/internal/target/repository_credential_source.go index 2ebb5449f..31284bf1e 100644 --- a/internal/target/repository_credential_source.go +++ b/internal/target/repository_credential_source.go @@ -43,19 +43,18 @@ func (r *Repository) AddTargetCredentialSources(ctx context.Context, targetId st return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", targetId))) } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) @@ -143,18 +142,16 @@ func (r *Repository) DeleteTargetCredentialSources(ctx context.Context, targetId return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", targetId))) } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { @@ -257,17 +254,16 @@ func (r *Repository) SetTargetCredentialSources(ctx context.Context, targetId st return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", targetId))) } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) diff --git a/internal/target/repository_host_source.go b/internal/target/repository_host_source.go index de02b7911..013097c76 100644 --- a/internal/target/repository_host_source.go +++ b/internal/target/repository_host_source.go @@ -40,18 +40,18 @@ func (r *Repository) AddTargetHostSources(ctx context.Context, targetId string, return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", targetId))) } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_CREATE.String()) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) @@ -137,18 +137,18 @@ func (r *Repository) DeleteTargetHostSources(ctx context.Context, targetId strin } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + metadata["op-type"] = append(metadata["op-type"], oplog.OpType_OP_TYPE_DELETE.String()) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) @@ -260,17 +260,17 @@ func (r *Repository) SetTargetHostSources(ctx context.Context, targetId string, } var metadata oplog.Metadata - var target interface{} - switch t.Type { - case TcpTargetType.String(): - tcpT := allocTcpTarget() - tcpT.PublicId = t.PublicId - tcpT.Version = targetVersion + 1 - target = &tcpT - metadata = tcpT.oplog(oplog.OpType_OP_TYPE_UPDATE) - default: + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { return nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unsupported target type %s", t.PublicId, t.Type)) } + + target := alloc() + target.SetPublicId(ctx, t.PublicId) + target.SetVersion(targetVersion + 1) + metadata = target.Oplog(oplog.OpType_OP_TYPE_UPDATE) + oplogWrapper, err := r.kms.GetWrapper(ctx, t.GetScopeId(), kms.KeyPurposeOplog) if err != nil { return nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) diff --git a/internal/target/repository_tcp_target.go b/internal/target/repository_tcp_target.go deleted file mode 100644 index 1f6fe63a2..000000000 --- a/internal/target/repository_tcp_target.go +++ /dev/null @@ -1,198 +0,0 @@ -package target - -import ( - "context" - "fmt" - "strings" - - "github.com/hashicorp/boundary/internal/db" - dbcommon "github.com/hashicorp/boundary/internal/db/common" - "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/oplog" -) - -// CreateTcpTarget inserts into the repository and returns the new Target with -// its list of host sets and credential libraries. -// WithHostSources and WithCredentialSources are the only supported option. -func (r *Repository) CreateTcpTarget(ctx context.Context, target *TcpTarget, opt ...Option) (Target, []HostSource, []CredentialSource, error) { - const op = "target.(Repository).CreateTcpTarget" - opts := getOpts(opt...) - if target == nil { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing target") - } - if target.TcpTarget == nil { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing target store") - } - if target.ScopeId == "" { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope id") - } - if target.Name == "" { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "missing name") - } - if target.PublicId != "" { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty") - } - - t := target.Clone().(*TcpTarget) - - if opts.withPublicId != "" { - if !strings.HasPrefix(opts.withPublicId, TcpTargetPrefix+"_") { - return nil, nil, nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("passed-in public ID %q has wrong prefix, should be %q", opts.withPublicId, TcpTargetPrefix)) - } - t.PublicId = opts.withPublicId - } else { - id, err := newTcpTargetId() - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op) - } - t.PublicId = id - } - - oplogWrapper, err := r.kms.GetWrapper(ctx, target.ScopeId, kms.KeyPurposeOplog) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper")) - } - - newHostSets := make([]interface{}, 0, len(opts.withHostSources)) - for _, hsId := range opts.withHostSources { - hostSet, err := NewTargetHostSet(t.PublicId, hsId) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory target host set")) - } - newHostSets = append(newHostSets, hostSet) - } - - newCredLibs := make([]interface{}, 0, len(opts.withCredentialSources)) - for _, clId := range opts.withCredentialSources { - credLib, err := NewCredentialLibrary(t.PublicId, clId) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to create in memory target credential library")) - } - newCredLibs = append(newCredLibs, credLib) - } - - metadata := t.oplog(oplog.OpType_OP_TYPE_CREATE) - var returnedTarget interface{} - var returnedHostSources []HostSource - var returnedCredSources []CredentialSource - _, err = r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(read db.Reader, w db.Writer) error { - targetTicket, err := w.GetTicket(t) - if err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to get ticket")) - } - msgs := make([]*oplog.Message, 0, 2) - var targetOplogMsg oplog.Message - returnedTarget = t.Clone() - if err := w.Create(ctx, returnedTarget, db.NewOplogMsg(&targetOplogMsg)); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to create target")) - } - msgs = append(msgs, &targetOplogMsg) - if len(newHostSets) > 0 { - hostSetOplogMsgs := make([]*oplog.Message, 0, len(newHostSets)) - if err := w.CreateItems(ctx, newHostSets, db.NewOplogMsgs(&hostSetOplogMsgs)); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add host sets")) - } - if returnedHostSources, err = fetchHostSources(ctx, read, t.PublicId); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to read host sources")) - } - msgs = append(msgs, hostSetOplogMsgs...) - } - if len(newCredLibs) > 0 { - credLibOplogMsgs := make([]*oplog.Message, 0, len(newCredLibs)) - if err := w.CreateItems(ctx, newCredLibs, db.NewOplogMsgs(&credLibOplogMsgs)); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to add credential sources")) - } - if returnedCredSources, err = fetchCredentialSources(ctx, read, t.PublicId); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to read credential sources")) - } - msgs = append(msgs, credLibOplogMsgs...) - } - if err := w.WriteOplogEntryWith(ctx, oplogWrapper, targetTicket, metadata, msgs); err != nil { - return errors.Wrap(ctx, err, op, errors.WithMsg("unable to write oplog")) - } - - return nil - }, - ) - if err != nil { - return nil, nil, nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s target id", t.PublicId))) - } - return returnedTarget.(*TcpTarget), returnedHostSources, returnedCredSources, nil -} - -// UpdateTcpTarget will update a target in the repository and return the written -// target. fieldMaskPaths provides field_mask.proto paths for fields that should -// be updated. Fields will be set to NULL if the field is a zero value and -// included in fieldMask. Name, Description, and WorkerFilter are the only -// updatable fields. If no updatable fields are included in the fieldMaskPaths, -// then an error is returned. -func (r *Repository) UpdateTcpTarget(ctx context.Context, target *TcpTarget, version uint32, fieldMaskPaths []string, _ ...Option) (Target, []HostSource, []CredentialSource, int, error) { - const op = "target.(Repository).UpdateTcpTarget" - if target == nil { - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target") - } - if target.TcpTarget == nil { - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target store") - } - if target.PublicId == "" { - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing target public id") - } - for _, f := range fieldMaskPaths { - switch { - case strings.EqualFold("name", f): - case strings.EqualFold("description", f): - case strings.EqualFold("defaultport", f): - case strings.EqualFold("sessionmaxseconds", f): - case strings.EqualFold("sessionconnectionlimit", f): - case strings.EqualFold("workerfilter", f): - default: - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, fmt.Sprintf("invalid field mask: %s", f)) - } - } - var dbMask, nullFields []string - dbMask, nullFields = dbcommon.BuildUpdatePaths( - map[string]interface{}{ - "Name": target.Name, - "Description": target.Description, - "DefaultPort": target.DefaultPort, - "SessionMaxSeconds": target.SessionMaxSeconds, - "SessionConnectionLimit": target.SessionConnectionLimit, - "WorkerFilter": target.WorkerFilter, - }, - fieldMaskPaths, - []string{"SessionMaxSeconds", "SessionConnectionLimit"}, - ) - if len(dbMask) == 0 && len(nullFields) == 0 { - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "empty field mask") - } - var returnedTarget Target - var rowsUpdated int - var hostSources []HostSource - var credSources []CredentialSource - _, err := r.writer.DoTx( - ctx, - db.StdRetryCnt, - db.ExpBackoff{}, - func(read db.Reader, w db.Writer) error { - var err error - t := target.Clone().(*TcpTarget) - returnedTarget, hostSources, credSources, rowsUpdated, err = r.update(ctx, t, version, dbMask, nullFields) - if err != nil { - return errors.Wrap(ctx, err, op) - } - return nil - }, - ) - if err != nil { - if errors.IsUniqueError(err) { - return nil, nil, nil, db.NoRowsAffected, errors.New(ctx, errors.NotUnique, op, fmt.Sprintf("target %s already exists in scope %s", target.Name, target.ScopeId)) - } - return nil, nil, nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", target.PublicId))) - } - return returnedTarget, hostSources, credSources, rowsUpdated, nil -} diff --git a/internal/target/repository_test.go b/internal/target/repository_test.go index 7530bd73a..6187421a3 100644 --- a/internal/target/repository_test.go +++ b/internal/target/repository_test.go @@ -1,17 +1,10 @@ package target import ( - "context" - "fmt" - "strconv" "testing" - "time" "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" - "github.com/hashicorp/boundary/internal/oplog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -93,336 +86,8 @@ func TestNewRepository(t *testing.T) { return } require.NoError(err) + assert.NotNil(got) assert.Equal(tt.want, got) }) } } - -func TestRepository_LookupTarget(t *testing.T) { - t.Parallel() - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - testKms := kms.TestKms(t, conn, wrapper) - iamRepo := iam.TestRepo(t, conn, wrapper) - _, proj := iam.TestScopes(t, iamRepo) - proj.Name = "project-name" - _, _, err := iamRepo.UpdateScope(context.Background(), proj, 1, []string{"name"}) - require.NoError(t, err) - rw := db.New(conn) - repo, err := NewRepository(rw, rw, testKms) - require.NoError(t, err) - tgt := TestTcpTarget(t, conn, proj.PublicId, "target-name") - - tests := []struct { - testName string - id string - name string - scopeId string - scopeName string - wantErr bool - }{ - { - testName: "id", - id: tgt.PublicId, - wantErr: false, - }, - { - testName: "name only", - name: tgt.Name, - wantErr: true, - }, - { - testName: "scope id only", - scopeId: proj.PublicId, - wantErr: true, - }, - { - testName: "scope name only", - scopeName: proj.Name, - wantErr: true, - }, - { - testName: "scope name and id", - scopeId: proj.PublicId, - scopeName: proj.Name, - wantErr: true, - }, - { - testName: "everything", - name: tgt.Name, - scopeId: proj.PublicId, - scopeName: proj.Name, - wantErr: true, - }, - { - testName: "name and scope name", - name: tgt.Name, - scopeName: proj.Name, - wantErr: false, - }, - { - testName: "name and scope id", - name: tgt.Name, - scopeId: proj.PublicId, - wantErr: false, - }, - { - testName: "id and name", - id: tgt.PublicId, - name: tgt.Name, - scopeId: proj.PublicId, - wantErr: true, - }, - { - testName: "id and scope name", - id: tgt.PublicId, - scopeName: proj.Name, - wantErr: true, - }, - { - testName: "id and scope id", - id: tgt.PublicId, - scopeId: proj.PublicId, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.testName, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - id := tt.id - if tt.name != "" && tt.id == "" { - id = tt.name - } - var opts []Option - if tt.name != "" { - opts = append(opts, WithName(tt.name)) - } - if tt.scopeId != "" { - opts = append(opts, WithScopeId(tt.scopeId)) - } - if tt.scopeName != "" { - opts = append(opts, WithScopeName(tt.scopeName)) - } - got, _, _, err := repo.LookupTarget(context.Background(), id, opts...) - if tt.wantErr { - require.Error(err) - return - } - require.NoError(err) - assert.Equal(tgt.PublicId, got.GetPublicId()) - }) - } -} - -func TestRepository_ListTargets(t *testing.T) { - t.Parallel() - conn, _ := db.TestSetup(t, "postgres") - const testLimit = 10 - wrapper := db.TestWrapper(t) - testKms := kms.TestKms(t, conn, wrapper) - iamRepo := iam.TestRepo(t, conn, wrapper) - _, proj := iam.TestScopes(t, iamRepo) - rw := db.New(conn) - repo, err := NewRepository(rw, rw, testKms) - require.NoError(t, err) - repo.defaultLimit = testLimit - - type args struct { - opt []Option - } - tests := []struct { - name string - createCnt int - createScopeId string - createScopeId2 string - grantUserId string - args args - wantCnt int - wantErr bool - }{ - { - name: "tcp-target", - createCnt: 5, - createScopeId: proj.PublicId, - args: args{ - opt: []Option{WithTargetType(TcpTargetType), WithScopeIds([]string{proj.PublicId})}, - }, - wantCnt: 5, - wantErr: false, - }, - { - name: "no-limit", - createCnt: testLimit + 1, - createScopeId: proj.PublicId, - args: args{ - opt: []Option{WithLimit(-1), WithScopeIds([]string{proj.PublicId})}, - }, - wantCnt: testLimit + 1, - wantErr: false, - }, - { - name: "default-limit", - createCnt: testLimit + 1, - createScopeId: proj.PublicId, - args: args{ - opt: []Option{WithScopeIds([]string{proj.PublicId})}, - }, - wantCnt: testLimit, - wantErr: false, - }, - { - name: "custom-limit", - createCnt: testLimit + 1, - createScopeId: proj.PublicId, - args: args{ - opt: []Option{WithLimit(3), WithScopeIds([]string{proj.PublicId})}, - }, - wantCnt: 3, - wantErr: false, - }, - { - name: "bad-org", - createCnt: 1, - createScopeId: proj.PublicId, - args: args{ - opt: []Option{WithScopeIds([]string{"bad-id"})}, - }, - wantCnt: 0, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert, require := assert.New(t), require.New(t) - require.NoError(conn.Where("1=1").Delete(allocTcpTarget()).Error) - testGroups := []*TcpTarget{} - for i := 0; i < tt.createCnt; i++ { - switch { - case tt.createScopeId2 != "" && i%2 == 0: - testGroups = append(testGroups, TestTcpTarget(t, conn, tt.createScopeId2, strconv.Itoa(i))) - default: - testGroups = append(testGroups, TestTcpTarget(t, conn, tt.createScopeId, strconv.Itoa(i))) - } - } - assert.Equal(tt.createCnt, len(testGroups)) - got, err := repo.ListTargets(context.Background(), tt.args.opt...) - if tt.wantErr { - require.Error(err) - return - } - require.NoError(err) - assert.Equal(tt.wantCnt, len(got)) - }) - } -} - -func TestRepository_ListRoles_Multiple_Scopes(t *testing.T) { - t.Parallel() - conn, _ := db.TestSetup(t, "postgres") - wrapper := db.TestWrapper(t) - testKms := kms.TestKms(t, conn, wrapper) - iamRepo := iam.TestRepo(t, conn, wrapper) - _, proj1 := iam.TestScopes(t, iamRepo) - _, proj2 := iam.TestScopes(t, iamRepo) - rw := db.New(conn) - repo, err := NewRepository(rw, rw, testKms) - require.NoError(t, err) - - require.NoError(t, conn.Where("1=1").Delete(allocTcpTarget()).Error) - - const numPerScope = 10 - var total int - for i := 0; i < numPerScope; i++ { - TestTcpTarget(t, conn, proj1.GetPublicId(), fmt.Sprintf("proj1-%d", i)) - total++ - TestTcpTarget(t, conn, proj2.GetPublicId(), fmt.Sprintf("proj2-%d", i)) - total++ - } - - got, err := repo.ListTargets(context.Background(), WithScopeIds([]string{"global", proj1.GetPublicId(), proj2.GetPublicId()})) - require.NoError(t, err) - assert.Equal(t, total, len(got)) -} - -func TestRepository_DeleteTarget(t *testing.T) { - t.Parallel() - conn, _ := db.TestSetup(t, "postgres") - rw := db.New(conn) - wrapper := db.TestWrapper(t) - testKms := kms.TestKms(t, conn, wrapper) - iamRepo := iam.TestRepo(t, conn, wrapper) - _, proj := iam.TestScopes(t, iamRepo) - repo, err := NewRepository(rw, rw, testKms) - require.NoError(t, err) - - type args struct { - target Target - opt []Option - } - tests := []struct { - name string - args args - wantRowsDeleted int - wantErr bool - wantErrMsg string - }{ - { - name: "valid", - args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "valid"), - }, - wantRowsDeleted: 1, - wantErr: false, - }, - { - name: "no-public-id", - args: args{ - target: func() Target { - target := allocTcpTarget() - return &target - }(), - }, - wantRowsDeleted: 0, - wantErr: true, - wantErrMsg: "target.(Repository).DeleteTarget: missing public id: parameter violation: error #100", - }, - { - name: "not-found", - args: args{ - target: func() Target { - id, err := newTcpTargetId() - require.NoError(t, err) - target := allocTcpTarget() - target.PublicId = id - return &target - }(), - }, - wantRowsDeleted: 0, - wantErr: true, - wantErrMsg: "db.LookupById: record not found, search issue: error #1100", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assert := assert.New(t) - deletedRows, err := repo.DeleteTarget(context.Background(), tt.args.target.GetPublicId(), tt.args.opt...) - if tt.wantErr { - assert.Error(err) - assert.Equal(0, deletedRows) - assert.Contains(err.Error(), tt.wantErrMsg) - err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) - assert.Error(err) - assert.True(errors.IsNotFoundError(err)) - return - } - assert.NoError(err) - assert.Equal(tt.wantRowsDeleted, deletedRows) - foundGroup, _, _, err := repo.LookupTarget(context.Background(), tt.args.target.GetPublicId()) - assert.NoError(err) - assert.Nil(foundGroup) - - err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) - assert.NoError(err) - }) - } -} diff --git a/internal/target/store/target.pb.go b/internal/target/store/target.pb.go index e248b8cd8..c3e8acdfd 100644 --- a/internal/target/store/target.pb.go +++ b/internal/target/store/target.pb.go @@ -253,157 +253,6 @@ func (x *TargetHostSet) GetCreateTime() *timestamp.Timestamp { return nil } -type TcpTarget struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // public_id is used to access the TargetTcp via an API - // @inject_tag: gorm:"primary_key" - PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` - // scope id for the TargetTcp - // @inject_tag: `gorm:"default:null"` - ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"` - // name is the optional friendly name used to - // access the TargetTcp via an API - // @inject_tag: `gorm:"default:null"` - Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` - // description of the TargetTcp - // @inject_tag: `gorm:"default:null"` - Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` - // create_time from the RDBMS - // @inject_tag: `gorm:"default:current_timestamp"` - CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` - // update_time from the RDBMS - // @inject_tag: `gorm:"default:current_timestamp"` - UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` - // version allows optimistic locking of the TargetTcp when modifying the - // TargetTcp - // @inject_tag: `gorm:"default:null"` - Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` - // default port of the TargetTcp - // @inject_tag: `gorm:"default:null"` - DefaultPort uint32 `protobuf:"varint,80,opt,name=default_port,json=defaultPort,proto3" json:"default_port,omitempty" gorm:"default:null"` - // Maximum total lifetime of a created session, in seconds - // @inject_tag: `gorm:"default:null"` - SessionMaxSeconds uint32 `protobuf:"varint,100,opt,name=session_max_seconds,json=sessionMaxSeconds,proto3" json:"session_max_seconds,omitempty" gorm:"default:null"` - // Maximum number of connections in a session - // @inject_tag: `gorm:"default:null"` - SessionConnectionLimit int32 `protobuf:"varint,110,opt,name=session_connection_limit,json=sessionConnectionLimit,proto3" json:"session_connection_limit,omitempty" gorm:"default:null"` - // A boolean expression that allows filtering the workers that can handle a session - // @inject_tag: `gorm:"default:null"` - WorkerFilter string `protobuf:"bytes,120,opt,name=worker_filter,json=workerFilter,proto3" json:"worker_filter,omitempty" gorm:"default:null"` -} - -func (x *TcpTarget) Reset() { - *x = TcpTarget{} - if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *TcpTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpTarget) ProtoMessage() {} - -func (x *TcpTarget) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpTarget.ProtoReflect.Descriptor instead. -func (*TcpTarget) Descriptor() ([]byte, []int) { - return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{2} -} - -func (x *TcpTarget) GetPublicId() string { - if x != nil { - return x.PublicId - } - return "" -} - -func (x *TcpTarget) GetScopeId() string { - if x != nil { - return x.ScopeId - } - return "" -} - -func (x *TcpTarget) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *TcpTarget) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *TcpTarget) GetCreateTime() *timestamp.Timestamp { - if x != nil { - return x.CreateTime - } - return nil -} - -func (x *TcpTarget) GetUpdateTime() *timestamp.Timestamp { - if x != nil { - return x.UpdateTime - } - return nil -} - -func (x *TcpTarget) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *TcpTarget) GetDefaultPort() uint32 { - if x != nil { - return x.DefaultPort - } - return 0 -} - -func (x *TcpTarget) GetSessionMaxSeconds() uint32 { - if x != nil { - return x.SessionMaxSeconds - } - return 0 -} - -func (x *TcpTarget) GetSessionConnectionLimit() int32 { - if x != nil { - return x.SessionConnectionLimit - } - return 0 -} - -func (x *TcpTarget) GetWorkerFilter() string { - if x != nil { - return x.WorkerFilter - } - return "" -} - type CredentialLibrary struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -426,7 +275,7 @@ type CredentialLibrary struct { func (x *CredentialLibrary) Reset() { *x = CredentialLibrary{} if protoimpl.UnsafeEnabled { - mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3] + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -439,7 +288,7 @@ func (x *CredentialLibrary) String() string { func (*CredentialLibrary) ProtoMessage() {} func (x *CredentialLibrary) ProtoReflect() protoreflect.Message { - mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[3] + mi := &file_controller_storage_target_store_v1_target_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -452,7 +301,7 @@ func (x *CredentialLibrary) ProtoReflect() protoreflect.Message { // Deprecated: Use CredentialLibrary.ProtoReflect.Descriptor instead. func (*CredentialLibrary) Descriptor() ([]byte, []int) { - return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{3} + return file_controller_storage_target_store_v1_target_proto_rawDescGZIP(), []int{2} } func (x *CredentialLibrary) GetTargetId() string { @@ -538,70 +387,25 @@ var file_controller_storage_target_store_v1_target_proto_rawDesc = []byte{ 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, 0xc6, 0x05, 0x0a, 0x09, 0x54, 0x63, 0x70, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 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, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, 0x0c, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, - 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x2a, 0xc2, - 0xdd, 0x29, 0x26, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x66, - 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x5c, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x64, 0x20, - 0x01, 0x28, 0x0d, 0x42, 0x2c, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x11, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, - 0x73, 0x52, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x70, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, - 0x18, 0x6e, 0x20, 0x01, 0x28, 0x05, 0x42, 0x36, 0xc2, 0xdd, 0x29, 0x32, 0x0a, 0x16, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x16, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x46, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, - 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xc2, - 0xdd, 0x29, 0x1d, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x12, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0xe0, - 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, - 0x72, 0x61, 0x72, 0x79, 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, 0x32, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, - 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, - 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, - 0x69, 0x6d, 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, 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, + 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xe0, 0x01, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 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, 0x32, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x5f, 0x69, + 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, + 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x70, 0x75, 0x72, 0x70, 0x6f, + 0x73, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x61, 0x6c, 0x50, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 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, 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 ( @@ -616,26 +420,23 @@ func file_controller_storage_target_store_v1_target_proto_rawDescGZIP() []byte { return file_controller_storage_target_store_v1_target_proto_rawDescData } -var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_controller_storage_target_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_controller_storage_target_store_v1_target_proto_goTypes = []interface{}{ (*TargetView)(nil), // 0: controller.storage.target.store.v1.TargetView (*TargetHostSet)(nil), // 1: controller.storage.target.store.v1.TargetHostSet - (*TcpTarget)(nil), // 2: controller.storage.target.store.v1.TcpTarget - (*CredentialLibrary)(nil), // 3: controller.storage.target.store.v1.CredentialLibrary - (*timestamp.Timestamp)(nil), // 4: controller.storage.timestamp.v1.Timestamp + (*CredentialLibrary)(nil), // 2: controller.storage.target.store.v1.CredentialLibrary + (*timestamp.Timestamp)(nil), // 3: controller.storage.timestamp.v1.Timestamp } var file_controller_storage_target_store_v1_target_proto_depIdxs = []int32{ - 4, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 3: controller.storage.target.store.v1.TcpTarget.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 4: controller.storage.target.store.v1.TcpTarget.update_time:type_name -> controller.storage.timestamp.v1.Timestamp - 4, // 5: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 3, // 0: controller.storage.target.store.v1.TargetView.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 1: controller.storage.target.store.v1.TargetView.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 2: controller.storage.target.store.v1.TargetHostSet.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 3, // 3: controller.storage.target.store.v1.CredentialLibrary.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_controller_storage_target_store_v1_target_proto_init() } @@ -669,18 +470,6 @@ func file_controller_storage_target_store_v1_target_proto_init() { } } file_controller_storage_target_store_v1_target_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TcpTarget); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_controller_storage_target_store_v1_target_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CredentialLibrary); i { case 0: return &v.state @@ -699,7 +488,7 @@ func file_controller_storage_target_store_v1_target_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controller_storage_target_store_v1_target_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/target/subtype.go b/internal/target/subtype.go deleted file mode 100644 index 79539757d..000000000 --- a/internal/target/subtype.go +++ /dev/null @@ -1,41 +0,0 @@ -package target - -import ( - "strings" -) - -type Subtype int - -const ( - UnknownSubtype Subtype = iota - TcpSubtype -) - -func (t Subtype) String() string { - switch t { - case TcpSubtype: - return "tcp" - } - return "unknown" -} - -// SubtypeFromType converts a string to a Subtype. -// returns UnknownSubtype if no Subtype with that name is found. -func SubtypeFromType(t string) Subtype { - switch { - case strings.EqualFold(strings.TrimSpace(t), TcpSubtype.String()): - return TcpSubtype - } - return UnknownSubtype -} - -// SubtypeFromId takes any public id in the target subsystem and uses the prefix to determine -// what subtype the id is for. -// Returns UnknownSubtype if no Subtype with this id's prefix is found. -func SubtypeFromId(id string) Subtype { - switch { - case strings.HasPrefix(strings.TrimSpace(id), TcpTargetPrefix): - return TcpSubtype - } - return UnknownSubtype -} diff --git a/internal/target/target.go b/internal/target/target.go index 7b27117d9..6916c43e0 100644 --- a/internal/target/target.go +++ b/internal/target/target.go @@ -1,12 +1,14 @@ package target import ( + "context" "fmt" "github.com/hashicorp/boundary/internal/db/timestamp" "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/oplog" "github.com/hashicorp/boundary/internal/target/store" + "github.com/hashicorp/boundary/internal/types/subtypes" ) // Target is a commmon interface for all target subtypes @@ -17,29 +19,25 @@ type Target interface { GetName() string GetDescription() string GetVersion() uint32 - GetType() string + GetType() subtypes.Subtype GetCreateTime() *timestamp.Timestamp GetUpdateTime() *timestamp.Timestamp GetSessionMaxSeconds() uint32 GetSessionConnectionLimit() int32 GetWorkerFilter() string - oplog(op oplog.OpType) oplog.Metadata -} - -// TargetType defines the possible types for targets. -type TargetType uint32 - -const ( - UnknownTargetType TargetType = 0 - TcpTargetType TargetType = 1 -) - -// String returns a string representation of the target type. -func (t TargetType) String() string { - return [...]string{ - "unknown", - "tcp", - }[t] + Clone() Target + SetPublicId(context.Context, string) error + SetScopeId(string) + SetName(string) + SetDescription(string) + SetVersion(uint32) + SetDefaultPort(uint32) + SetCreateTime(*timestamp.Timestamp) + SetUpdateTime(*timestamp.Timestamp) + SetSessionMaxSeconds(uint32) + SetSessionConnectionLimit(int32) + SetWorkerFilter(string) + Oplog(op oplog.OpType) oplog.Metadata } const ( @@ -79,24 +77,32 @@ func (t *targetView) SetTableName(n string) { } } -// targetSubtype converts the target view to the concrete subtype -func (t *targetView) targetSubtype() (Target, error) { - const op = "target.targetView.targetSubtype" - switch t.Type { - case TcpTargetType.String(): - tcpTarget := allocTcpTarget() - tcpTarget.PublicId = t.PublicId - tcpTarget.ScopeId = t.ScopeId - tcpTarget.Name = t.Name - tcpTarget.Description = t.Description - tcpTarget.DefaultPort = t.DefaultPort - tcpTarget.CreateTime = t.CreateTime - tcpTarget.UpdateTime = t.UpdateTime - tcpTarget.Version = t.Version - tcpTarget.SessionMaxSeconds = t.SessionMaxSeconds - tcpTarget.SessionConnectionLimit = t.SessionConnectionLimit - tcpTarget.WorkerFilter = t.WorkerFilter - return &tcpTarget, nil - } - return nil, errors.NewDeprecated(errors.InvalidParameter, op, fmt.Sprintf("%s is an unknown target subtype of %s", t.PublicId, t.Type)) +func (t *targetView) Subtype() subtypes.Subtype { + return subtypes.Subtype(t.Type) +} + +// targetSubtype converts the target view to the concrete subtype +func (t *targetView) targetSubtype(ctx context.Context) (Target, error) { + const op = "target.targetView.targetSubtype" + + alloc, ok := subtypeRegistry.allocFunc(t.Subtype()) + if !ok { + return nil, errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("%s is an unknown target subtype of %s", t.PublicId, t.Type)) + } + + tt := alloc() + if err := tt.SetPublicId(ctx, t.PublicId); err != nil { + return nil, err + } + tt.SetVersion(t.Version) + tt.SetScopeId(t.ScopeId) + tt.SetName(t.Name) + tt.SetDescription(t.Description) + tt.SetDefaultPort(t.DefaultPort) + tt.SetCreateTime(t.CreateTime) + tt.SetUpdateTime(t.UpdateTime) + tt.SetSessionMaxSeconds(t.SessionMaxSeconds) + tt.SetSessionConnectionLimit(t.SessionConnectionLimit) + tt.SetWorkerFilter(t.WorkerFilter) + return tt, nil } diff --git a/internal/target/target_test.go b/internal/target/target_test.go index d3a287c38..a5366905d 100644 --- a/internal/target/target_test.go +++ b/internal/target/target_test.go @@ -1,15 +1,16 @@ -package target +package target_test import ( "testing" + "github.com/hashicorp/boundary/internal/target" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestTarget_SetTableName(t *testing.T) { t.Parallel() - defaultTableName := targetsViewDefaultTable + defaultTableName := target.TargetsViewDefaultTable tests := []struct { name string setNameTo string @@ -29,9 +30,9 @@ func TestTarget_SetTableName(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - def := allocTargetView() + def := target.AllocTargetView() require.Equal(defaultTableName, def.TableName()) - s := allocTargetView() + s := target.AllocTargetView() s.SetTableName(tt.setNameTo) assert.Equal(tt.want, s.TableName()) }) diff --git a/internal/target/tcp/exports_test.go b/internal/target/tcp/exports_test.go new file mode 100644 index 000000000..023d2683c --- /dev/null +++ b/internal/target/tcp/exports_test.go @@ -0,0 +1,19 @@ +package tcp + +import "github.com/hashicorp/boundary/internal/target" + +// Expose functions and variables for tests. +var ( + TestId = testId + TestTargetName = testTargetName + DefaultTableName = defaultTableName +) + +// NewTestTarget is a test helper that bypasses the scopeId checks +// performed by NewTarget, allowing tests to create Targets with +// nil scopeIds for more robust testing. +func NewTestTarget(scopeId string, opt ...target.Option) *Target { + t, _ := New("testScope", opt...) + t.ScopeId = scopeId + return t +} diff --git a/internal/target/immutable_fields_test.go b/internal/target/tcp/immutable_fields_test.go similarity index 75% rename from internal/target/immutable_fields_test.go rename to internal/target/tcp/immutable_fields_test.go index 865f1d3a1..f268806e1 100644 --- a/internal/target/immutable_fields_test.go +++ b/internal/target/tcp/immutable_fields_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -9,6 +9,8 @@ import ( "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -24,17 +26,17 @@ func TestTarget_ImmutableFields(t *testing.T) { ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - new := TestTcpTarget(t, conn, proj.PublicId, testId(t)) + new := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestId(t)) tests := []struct { name string - update *TcpTarget + update *tcp.Target fieldMask []string }{ { name: "public_id", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.PublicId = "p_thisIsNotAValidId" return target }(), @@ -42,8 +44,8 @@ func TestTarget_ImmutableFields(t *testing.T) { }, { name: "create time", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.CreateTime = &ts return target }(), @@ -51,8 +53,8 @@ func TestTarget_ImmutableFields(t *testing.T) { }, { name: "scope_id", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.ScopeId = "o_thisIsNotAValidId" return target }(), @@ -65,7 +67,7 @@ func TestTarget_ImmutableFields(t *testing.T) { assert, require := assert.New(t), require.New(t) orig := new.Clone() - orig.(*TcpTarget).SetTableName("target") + orig.(*tcp.Target).SetTableName("target") err := rw.LookupById(context.Background(), orig) require.NoError(err) @@ -75,11 +77,11 @@ func TestTarget_ImmutableFields(t *testing.T) { assert.Equal(0, rowsUpdated) after := new.Clone() - after.(*TcpTarget).SetTableName("target") + after.(*tcp.Target).SetTableName("target") err = rw.LookupById(context.Background(), after) require.NoError(err) - assert.True(proto.Equal(orig.(*TcpTarget), after.(*TcpTarget))) + assert.True(proto.Equal(orig.(*tcp.Target), after.(*tcp.Target))) }) } } @@ -93,17 +95,17 @@ func TestTcpTarget_ImmutableFields(t *testing.T) { ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - new := TestTcpTarget(t, conn, proj.PublicId, testId(t)) + new := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestId(t)) tests := []struct { name string - update *TcpTarget + update *tcp.Target fieldMask []string }{ { name: "public_id", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.PublicId = "p_thisIsNotAValidId" return target }(), @@ -111,8 +113,8 @@ func TestTcpTarget_ImmutableFields(t *testing.T) { }, { name: "create time", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.CreateTime = &ts return target }(), @@ -120,8 +122,8 @@ func TestTcpTarget_ImmutableFields(t *testing.T) { }, { name: "scope_id", - update: func() *TcpTarget { - target := new.Clone().(*TcpTarget) + update: func() *tcp.Target { + target := new.Clone().(*tcp.Target) target.ScopeId = "o_thisIsNotAValidId" return target }(), @@ -144,7 +146,7 @@ func TestTcpTarget_ImmutableFields(t *testing.T) { err = rw.LookupById(context.Background(), after) require.NoError(err) - assert.True(proto.Equal(orig.(*TcpTarget), after.(*TcpTarget))) + assert.True(proto.Equal(orig.(*tcp.Target), after.(*tcp.Target))) }) } } @@ -155,35 +157,35 @@ func TestTargetHostSet_ImmutableFields(t *testing.T) { wrapper := db.TestWrapper(t) testKms := kms.TestKms(t, conn, wrapper) rw := db.New(conn) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) ts := timestamp.Timestamp{Timestamp: ×tamppb.Timestamp{Seconds: 0, Nanos: 0}} _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - projTarget := TestTcpTarget(t, conn, proj.PublicId, testId(t)) + projTarget := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestId(t)) testCats := static.TestCatalogs(t, conn, proj.PublicId, 1) hsets := static.TestSets(t, conn, testCats[0].GetPublicId(), 2) require.Equal(t, 2, len(hsets)) - updateTarget := TestTcpTarget(t, conn, proj.PublicId, testId(t)) + updateTarget := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestId(t)) updateHset := hsets[1] _, gotHostSources, _, err := repo.AddTargetHostSources(context.Background(), projTarget.PublicId, 1, []string{hsets[0].PublicId}) require.NoError(t, err) require.Equal(t, 1, len(gotHostSources)) - new, err := NewTargetHostSet(projTarget.PublicId, gotHostSources[0].Id()) + new, err := target.NewTargetHostSet(projTarget.PublicId, gotHostSources[0].Id()) require.NoError(t, err) tests := []struct { name string - update *TargetHostSet + update *target.TargetHostSet fieldMask []string }{ { name: "target_id", - update: func() *TargetHostSet { - target := new.Clone().(*TargetHostSet) + update: func() *target.TargetHostSet { + target := new.Clone().(*target.TargetHostSet) target.TargetId = updateTarget.PublicId return target }(), @@ -191,8 +193,8 @@ func TestTargetHostSet_ImmutableFields(t *testing.T) { }, { name: "create time", - update: func() *TargetHostSet { - target := new.Clone().(*TargetHostSet) + update: func() *target.TargetHostSet { + target := new.Clone().(*target.TargetHostSet) target.CreateTime = &ts return target }(), @@ -200,8 +202,8 @@ func TestTargetHostSet_ImmutableFields(t *testing.T) { }, { name: "host_set_id", - update: func() *TargetHostSet { - target := new.Clone().(*TargetHostSet) + update: func() *target.TargetHostSet { + target := new.Clone().(*target.TargetHostSet) target.HostSetId = updateHset.PublicId return target }(), @@ -223,7 +225,7 @@ func TestTargetHostSet_ImmutableFields(t *testing.T) { after := new.Clone() err = rw.LookupWhere(context.Background(), after, "target_id = ? and host_set_id = ?", new.TargetId, new.HostSetId) require.NoError(err) - assert.True(proto.Equal(orig.(*TargetHostSet), after.(*TargetHostSet))) + assert.True(proto.Equal(orig.(*target.TargetHostSet), after.(*target.TargetHostSet))) }) } } diff --git a/internal/target/tcp/register.go b/internal/target/tcp/register.go new file mode 100644 index 000000000..68508d430 --- /dev/null +++ b/internal/target/tcp/register.go @@ -0,0 +1,37 @@ +package tcp + +import ( + "context" + + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/target" +) + +func init() { + target.Register(Subtype, allocTarget, vet, TargetPrefix) +} + +const ( + // TargetPrefix is the prefix for public ids of a tcp.Target. + TargetPrefix = "ttcp" +) + +// vet validates that the given target.Target is a tcp.Target and that it +// has a Target store. +func vet(ctx context.Context, t target.Target) error { + const op = "tcp.vet" + + tt, ok := t.(*Target) + if !ok { + return errors.New(ctx, errors.InvalidParameter, op, "target is not a tcp.Target") + } + + if tt == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing target") + } + + if tt.Target == nil { + return errors.New(ctx, errors.InvalidParameter, op, "missing target store") + } + return nil +} diff --git a/internal/target/repository_credential_source_test.go b/internal/target/tcp/repository_credential_source_test.go similarity index 89% rename from internal/target/repository_credential_source_test.go rename to internal/target/tcp/repository_credential_source_test.go index 4a0a39cc1..ba0720773 100644 --- a/internal/target/repository_credential_source_test.go +++ b/internal/target/tcp/repository_credential_source_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -25,7 +27,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { testKms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, staticProj := iam.TestScopes(t, iamRepo) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) cs := vault.TestCredentialStores(t, conn, wrapper, staticProj.GetPublicId(), 1)[0] @@ -103,7 +105,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - projTarget := TestTcpTarget(t, conn, staticProj.PublicId, tt.name) + projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, tt.name) gotTarget, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, tt.args.targetVersion, tt.args.credLibIds) if tt.wantErr { require.Error(err) @@ -112,7 +114,7 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { } require.NoError(err) assert.Len(gotCredSources, len(tt.wantCredLibIds)) - gotCredSourcesMap := map[string]CredentialSource{} + gotCredSourcesMap := map[string]target.CredentialSource{} for _, s := range gotCredSources { gotCredSourcesMap[s.Id()] = s } @@ -124,26 +126,22 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { err = db.TestVerifyOplog(t, rw, projTarget.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) - foundCredSources, err := fetchCredentialSources(context.Background(), rw, projTarget.PublicId) + tar, _, lookupCredSources, err := repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) - assert.Len(foundCredSources, len(gotCredSourcesMap)) - for _, s := range foundCredSources { + assert.Equal(tt.args.targetVersion+1, tar.GetVersion()) + assert.Equal(projTarget.GetVersion(), tar.GetVersion()-1) + assert.True(proto.Equal(gotTarget.(*tcp.Target), tar.(*tcp.Target))) + assert.Equal(gotCredSources, lookupCredSources) + for _, s := range lookupCredSources { assert.NotEmpty(gotCredSourcesMap[s.Id()]) assert.Equal(projTarget.PublicId, s.TargetId()) } - - target, _, lookupCredSources, err := repo.LookupTarget(context.Background(), projTarget.PublicId) - require.NoError(err) - assert.Equal(tt.args.targetVersion+1, target.GetVersion()) - assert.Equal(projTarget.GetVersion(), target.GetVersion()-1) - assert.True(proto.Equal(gotTarget.(*TcpTarget), target.(*TcpTarget))) - assert.Equal(gotCredSources, lookupCredSources) }) } t.Run("add-existing", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - projTarget := TestTcpTarget(t, conn, staticProj.PublicId, "add-existing") + projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, "add-existing") _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []string{lib1.PublicId}) require.NoError(err) assert.Len(gotCredSources, 1) @@ -160,10 +158,10 @@ func TestRepository_AddTargetCredentialSources(t *testing.T) { assert.True(errors.Match(errors.T(errors.NotUnique), err)) // Previous transactions should have been rolled back and only lib1 should be associated - gotCredSources, err = fetchCredentialSources(context.Background(), rw, projTarget.PublicId) + _, _, lookupCredSources, err := repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) - assert.Len(gotCredSources, 1) - assert.Equal(lib1.PublicId, gotCredSources[0].Id()) + assert.Len(lookupCredSources, 1) + assert.Equal(lib1.PublicId, lookupCredSources[0].Id()) }) t.Run("target-not-found", func(t *testing.T) { assert, require := assert.New(t), require.New(t) @@ -183,7 +181,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { testKms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) type args struct { @@ -229,7 +227,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { { name: "not-found", args: args{ - targetIdOverride: func() *string { id := testId(t); return &id }(), + targetIdOverride: func() *string { id := tcp.TestId(t); return &id }(), createCnt: 5, deleteCnt: 5, }, @@ -277,7 +275,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { assert, require := assert.New(t), require.New(t) cs := css[i] - target := TestTcpTarget(t, conn, proj.PublicId, tt.name) + target := tcp.TestTarget(t, conn, proj.PublicId, tt.name) clIds := make([]string, 0, tt.args.createCnt) if tt.args.createCnt > 0 { @@ -334,7 +332,7 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { lib2 := libs[1] lib3 := libs[2] - projTarget := TestTcpTarget(t, conn, proj.PublicId, "add-existing") + projTarget := tcp.TestTarget(t, conn, proj.PublicId, "add-existing") _, _, gotCredSources, err := repo.AddTargetCredentialSources(context.Background(), projTarget.PublicId, 1, []string{lib1.PublicId, lib2.PublicId}) require.NoError(err) assert.Len(gotCredSources, 2) @@ -352,9 +350,9 @@ func TestRepository_DeleteTargetCredentialSources(t *testing.T) { assert.Equal(0, delCount) // Previous transactions should have been rolled back and only lib1 should be associated - gotCredSources, err = fetchCredentialSources(context.Background(), rw, projTarget.PublicId) + _, _, lookupCredSources, err := repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) - assert.Len(gotCredSources, 2) + assert.Len(lookupCredSources, 2) }) } @@ -364,7 +362,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) testKms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) iamRepo := iam.TestRepo(t, conn, wrapper) @@ -375,7 +373,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { lib1 := credLibs[0] lib2 := credLibs[1] - setupFn := func(target Target) []CredentialSource { + setupFn := func(target target.Target) []target.CredentialSource { credLibs := vault.TestCredentialLibraries(t, conn, wrapper, cs.GetPublicId(), 10) clIds := make([]string, 0, len(credLibs)) for _, cl := range credLibs { @@ -394,7 +392,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { } tests := []struct { name string - setup func(Target) []CredentialSource + setup func(target.Target) []target.CredentialSource args args wantAffectedRows int wantErr bool @@ -470,11 +468,11 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - target := TestTcpTarget(t, conn, proj.PublicId, tt.name) + tar := tcp.TestTarget(t, conn, proj.PublicId, tt.name) - var origCredSources []CredentialSource + var origCredSources []target.CredentialSource if tt.setup != nil { - origCredSources = tt.setup(target) + origCredSources = tt.setup(tar) } if tt.args.addToOrigLibs { origIds := make([]string, 0, len(origCredSources)) @@ -484,11 +482,11 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { tt.args.clIds = append(tt.args.clIds, origIds...) } - origTarget, _, lookupCredSources, err := repo.LookupTarget(context.Background(), target.GetPublicId()) + origTarget, _, lookupCredSources, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) require.NoError(err) assert.Equal(origCredSources, lookupCredSources) - _, got, affectedRows, err := repo.SetTargetCredentialSources(context.Background(), target.GetPublicId(), tt.args.targetVersion, tt.args.clIds) + _, got, affectedRows, err := repo.SetTargetCredentialSources(context.Background(), tar.GetPublicId(), tt.args.targetVersion, tt.args.clIds) if tt.wantErr { require.Error(err) assert.Equal(0, affectedRows) @@ -514,7 +512,7 @@ func TestRepository_SetTargetCredentialSources(t *testing.T) { sort.Strings(gotIds) assert.Equal(wantIds, gotIds) - foundTarget, _, _, err := repo.LookupTarget(context.Background(), target.GetPublicId()) + foundTarget, _, _, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) require.NoError(err) if tt.name != "no-change" { assert.Equalf(tt.args.targetVersion+1, foundTarget.GetVersion(), "%s unexpected version: %d/%d", tt.name, tt.args.targetVersion+1, foundTarget.GetVersion()) diff --git a/internal/target/repository_host_source_test.go b/internal/target/tcp/repository_host_source_test.go similarity index 85% rename from internal/target/repository_host_source_test.go rename to internal/target/tcp/repository_host_source_test.go index b5edc579d..d0a2b3fa9 100644 --- a/internal/target/repository_host_source_test.go +++ b/internal/target/tcp/repository_host_source_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -12,11 +12,20 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + targetstore "github.com/hashicorp/boundary/internal/target/store" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) +func allocTargetHostSet() target.TargetHostSet { + return target.TargetHostSet{ + TargetHostSet: &targetstore.TargetHostSet{}, + } +} + func TestRepository_AddTargetHostSets(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") @@ -25,7 +34,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) { testKms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) staticOrg, staticProj := iam.TestScopes(t, iamRepo) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) createHostSetsFn := func(orgs, projects []string) []string { @@ -46,7 +55,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) { type args struct { targetVersion uint32 wantTargetIds bool - opt []Option + opt []target.Option } tests := []struct { name string @@ -90,9 +99,9 @@ func TestRepository_AddTargetHostSets(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) require.NoError(conn.Where("1=1").Delete(allocTargetHostSet()).Error) - require.NoError(conn.Where("1=1").Delete(allocTcpTarget()).Error) + require.NoError(conn.Where("1=1").Delete(tcp.NewTestTarget("")).Error) - projTarget := TestTcpTarget(t, conn, staticProj.PublicId, "static-proj") + projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, "static-proj") var hostSourceIds []string origTarget, origHostSet, _, err := repo.LookupTarget(context.Background(), projTarget.PublicId) @@ -124,7 +133,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) { return } require.NoError(err) - gotHostSet := map[string]HostSource{} + gotHostSet := map[string]target.HostSource{} for _, s := range gotHostSources { gotHostSet[s.Id()] = s } @@ -141,18 +150,15 @@ func TestRepository_AddTargetHostSets(t *testing.T) { err = db.TestVerifyOplog(t, rw, projTarget.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) - foundHostSources, err := fetchHostSources(context.Background(), rw, projTarget.PublicId) + tar, ths, _, err := repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) - for _, s := range foundHostSources { + assert.Equal(tt.args.targetVersion+1, tar.GetVersion()) + assert.Equal(origTarget.GetVersion(), tar.GetVersion()-1) + assert.Equal(gotHostSources, ths) + assert.True(proto.Equal(gotTarget.(*tcp.Target), tar.(*tcp.Target))) + for _, s := range ths { assert.NotEmpty(gotHostSet[s.Id()]) } - - target, ths, _, err := repo.LookupTarget(context.Background(), projTarget.PublicId) - require.NoError(err) - assert.Equal(tt.args.targetVersion+1, target.GetVersion()) - assert.Equal(origTarget.GetVersion(), target.GetVersion()-1) - assert.Equal(gotHostSources, ths) - assert.True(proto.Equal(gotTarget.(*TcpTarget), target.(*TcpTarget))) }) } t.Run("add-existing", func(t *testing.T) { @@ -164,7 +170,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) { hs2 := hsets[1] hs3 := hsets[2] - projTarget := TestTcpTarget(t, conn, staticProj.PublicId, "add-existing") + projTarget := tcp.TestTarget(t, conn, staticProj.PublicId, "add-existing") _, gotHostSources, _, err := repo.AddTargetHostSources(context.Background(), projTarget.PublicId, 1, []string{hs1.PublicId}) require.NoError(err) assert.Len(gotHostSources, 1) @@ -181,7 +187,7 @@ func TestRepository_AddTargetHostSets(t *testing.T) { assert.True(errors.Match(errors.T(errors.NotUnique), err)) // Previous transactions should have been rolled back and only hs1 should be associated - gotHostSources, err = fetchHostSources(context.Background(), rw, projTarget.PublicId) + _, gotHostSources, _, err = repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) assert.Len(gotHostSources, 1) assert.Equal(hs1.PublicId, gotHostSources[0].Id()) @@ -196,16 +202,16 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { testKms := kms.TestKms(t, conn, wrapper) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) type args struct { - target Target + target target.Target targetIdOverride *string targetVersionOverride *uint32 createCnt int deleteCnt int - opt []Option + opt []target.Option } tests := []struct { name string @@ -217,7 +223,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "valid", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "valid"), + target: tcp.TestTarget(t, conn, proj.PublicId, "valid"), createCnt: 5, deleteCnt: 5, }, @@ -227,7 +233,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "valid-keeping-some", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "valid-keeping-some"), + target: tcp.TestTarget(t, conn, proj.PublicId, "valid-keeping-some"), createCnt: 5, deleteCnt: 2, }, @@ -237,7 +243,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "no-deletes", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "no-deletes"), + target: tcp.TestTarget(t, conn, proj.PublicId, "no-deletes"), createCnt: 5, }, wantRowsDeleted: 0, @@ -247,8 +253,8 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "not-found", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "not-found"), - targetIdOverride: func() *string { id := testId(t); return &id }(), + target: tcp.TestTarget(t, conn, proj.PublicId, "not-found"), + targetIdOverride: func() *string { id := tcp.TestId(t); return &id }(), createCnt: 5, deleteCnt: 5, }, @@ -258,7 +264,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "missing-target-id", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "missing-target-id"), + target: tcp.TestTarget(t, conn, proj.PublicId, "missing-target-id"), targetIdOverride: func() *string { id := ""; return &id }(), createCnt: 5, deleteCnt: 5, @@ -270,7 +276,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "zero-version", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "zero-version"), + target: tcp.TestTarget(t, conn, proj.PublicId, "zero-version"), targetVersionOverride: func() *uint32 { v := uint32(0); return &v }(), createCnt: 5, deleteCnt: 5, @@ -282,7 +288,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { { name: "bad-version", args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "bad-version"), + target: tcp.TestTarget(t, conn, proj.PublicId, "bad-version"), targetVersionOverride: func() *uint32 { v := uint32(1000); return &v }(), createCnt: 5, deleteCnt: 5, @@ -367,7 +373,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { hs2 := hsets[1] hs3 := hsets[2] - projTarget := TestTcpTarget(t, conn, proj.PublicId, "delete-unassociated") + projTarget := tcp.TestTarget(t, conn, proj.PublicId, "delete-unassociated") _, gotHostSources, _, err := repo.AddTargetHostSources(context.Background(), projTarget.PublicId, 1, []string{hs1.PublicId, hs2.PublicId}) require.NoError(err) assert.Len(gotHostSources, 2) @@ -386,7 +392,7 @@ func TestRepository_DeleteTargetHosts(t *testing.T) { assert.Equal(0, delCount) // Previous transactions should have been rolled back - gotHostSources, err = fetchHostSources(context.Background(), rw, projTarget.PublicId) + _, gotHostSources, _, err = repo.LookupTarget(context.Background(), projTarget.PublicId) require.NoError(err) assert.Len(gotHostSources, 2) }) @@ -398,7 +404,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { rw := db.New(conn) wrapper := db.TestWrapper(t) testKms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) iamRepo := iam.TestRepo(t, conn, wrapper) @@ -421,7 +427,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { return results } - setupFn := func(target Target) []HostSource { + setupFn := func(target target.Target) []target.HostSource { hs := createHostSetsFn() _, created, _, err := repo.AddTargetHostSources(context.Background(), target.GetPublicId(), 1, hs) require.NoError(t, err) @@ -429,15 +435,15 @@ func TestRepository_SetTargetHostSets(t *testing.T) { return created } type args struct { - target Target + target target.Target targetVersion uint32 hostSourceIds []string addToOrigHostSources bool - opt []Option + opt []target.Option } tests := []struct { name string - setup func(Target) []HostSource + setup func(target.Target) []target.HostSource args args wantAffectedRows int wantErr bool @@ -446,7 +452,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { name: "clear", setup: setupFn, args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "clear"), + target: tcp.TestTarget(t, conn, proj.PublicId, "clear"), targetVersion: 2, // yep, since setupFn will increment it to 2 hostSourceIds: []string{}, }, @@ -457,7 +463,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { name: "no-change", setup: setupFn, args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "no-change"), + target: tcp.TestTarget(t, conn, proj.PublicId, "no-change"), targetVersion: 2, // yep, since setupFn will increment it to 2 hostSourceIds: []string{}, addToOrigHostSources: true, @@ -469,7 +475,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { name: "add-sets", setup: setupFn, args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "add-sets"), + target: tcp.TestTarget(t, conn, proj.PublicId, "add-sets"), targetVersion: 2, // yep, since setupFn will increment it to 2 hostSourceIds: []string{testHostSetIds[0], testHostSetIds[1]}, addToOrigHostSources: true, @@ -481,7 +487,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { name: "add host sets with zero version", setup: setupFn, args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "add host sets with zero version"), + target: tcp.TestTarget(t, conn, proj.PublicId, "add host sets with zero version"), targetVersion: 0, hostSourceIds: []string{testHostSetIds[0], testHostSetIds[1]}, addToOrigHostSources: true, @@ -492,7 +498,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { name: "remove existing and add users and grps", setup: setupFn, args: args{ - target: TestTcpTarget(t, conn, proj.PublicId, "remove existing and add host sets"), + target: tcp.TestTarget(t, conn, proj.PublicId, "remove existing and add host sets"), targetVersion: 2, // yep, since setupFn will increment it to 2 hostSourceIds: []string{testHostSetIds[0], testHostSetIds[1]}, addToOrigHostSources: false, @@ -504,7 +510,7 @@ func TestRepository_SetTargetHostSets(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - var origHostSources []HostSource + var origHostSources []target.HostSource if tt.setup != nil { origHostSources = tt.setup(tt.args.target) } diff --git a/internal/target/repository_tcp_target_test.go b/internal/target/tcp/repository_tcp_target_test.go similarity index 68% rename from internal/target/repository_tcp_target_test.go rename to internal/target/tcp/repository_tcp_target_test.go index 89b34dfce..21b5806ed 100644 --- a/internal/target/repository_tcp_target_test.go +++ b/internal/target/tcp/repository_tcp_target_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -13,18 +13,20 @@ import ( "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) -func TestRepository_CreateTcpTarget(t *testing.T) { +func TestRepository_CreateTarget(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) wrapper := db.TestWrapper(t) testKms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) @@ -43,8 +45,8 @@ func TestRepository_CreateTcpTarget(t *testing.T) { } type args struct { - target *TcpTarget - opt []Option + target *tcp.Target + opt []target.Option } tests := []struct { name string @@ -57,11 +59,11 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "valid-org", args: args{ - target: func() *TcpTarget { - target, err := NewTcpTarget(proj.PublicId, - WithName("valid-org"), - WithDescription("valid-org"), - WithDefaultPort(uint32(22))) + target: func() *tcp.Target { + target, err := tcp.New(proj.PublicId, + target.WithName("valid-org"), + target.WithDescription("valid-org"), + target.WithDefaultPort(uint32(22))) require.NoError(t, err) return target }(), @@ -73,15 +75,15 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "valid-org-with-host-sets", args: args{ - target: func() *TcpTarget { - target, err := NewTcpTarget(proj.PublicId, - WithName("valid-org-with-host-sets"), - WithDescription("valid-org"), - WithDefaultPort(uint32(22))) + target: func() *tcp.Target { + target, err := tcp.New(proj.PublicId, + target.WithName("valid-org-with-host-sets"), + target.WithDescription("valid-org"), + target.WithDefaultPort(uint32(22))) require.NoError(t, err) return target }(), - opt: []Option{WithHostSources(sets)}, + opt: []target.Option{target.WithHostSources(sets)}, }, wantErr: false, wantHostSources: sets, @@ -90,15 +92,15 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "valid-org-with-cred-libs", args: args{ - target: func() *TcpTarget { - target, err := NewTcpTarget(proj.PublicId, - WithName("valid-org-with-cred-libs"), - WithDescription("valid-org"), - WithDefaultPort(uint32(22))) + target: func() *tcp.Target { + target, err := tcp.New(proj.PublicId, + target.WithName("valid-org-with-cred-libs"), + target.WithDescription("valid-org"), + target.WithDefaultPort(uint32(22))) require.NoError(t, err) return target }(), - opt: []Option{WithCredentialSources(clIds)}, + opt: []target.Option{target.WithCredentialSources(clIds)}, }, wantErr: false, wantCredLibs: clIds, @@ -107,17 +109,17 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "valid-org-with-cred-libs-and-host-sets", args: args{ - target: func() *TcpTarget { - target, err := NewTcpTarget(proj.PublicId, - WithName("valid-org-with-cred-libs-and-host-sets"), - WithDescription("valid-org"), - WithDefaultPort(uint32(22))) + target: func() *tcp.Target { + target, err := tcp.New(proj.PublicId, + target.WithName("valid-org-with-cred-libs-and-host-sets"), + target.WithDescription("valid-org"), + target.WithDefaultPort(uint32(22))) require.NoError(t, err) return target }(), - opt: []Option{ - WithHostSources(sets), - WithCredentialSources(clIds), + opt: []target.Option{ + target.WithHostSources(sets), + target.WithCredentialSources(clIds), }, }, wantErr: false, @@ -135,8 +137,8 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "nil-target-store", args: args{ - target: func() *TcpTarget { - target := &TcpTarget{} + target: func() *tcp.Target { + target := &tcp.Target{} return target }(), }, @@ -146,13 +148,18 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "public-id-not-empty", args: args{ - target: func() *TcpTarget { - target, err := NewTcpTarget(proj.PublicId, WithName("valid-org"), WithDescription("valid-org"), WithDefaultPort(uint32(22))) + target: func() *tcp.Target { + tar, err := tcp.New( + proj.PublicId, + target.WithName("valid-org"), + target.WithDescription("valid-org"), + target.WithDefaultPort(uint32(22)), + ) require.NoError(t, err) - id, err := newTcpTargetId() + id, err := db.NewPublicId(tcp.TargetPrefix) require.NoError(t, err) - target.PublicId = id - return target + tar.PublicId = id + return tar }(), }, wantErr: true, @@ -161,11 +168,14 @@ func TestRepository_CreateTcpTarget(t *testing.T) { { name: "empty-scope-id", args: args{ - target: func() *TcpTarget { - target := allocTcpTarget() - target.Name = "empty-scope-id" + target: func() *tcp.Target { + tar, err := tcp.New( + proj.PublicId, + target.WithName("empty-scope-id"), + ) require.NoError(t, err) - return &target + tar.ScopeId = "" + return tar }(), }, wantErr: true, @@ -175,15 +185,15 @@ func TestRepository_CreateTcpTarget(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - target, hostSources, credSources, err := repo.CreateTcpTarget(context.Background(), tt.args.target, tt.args.opt...) + tar, hostSources, credSources, err := repo.CreateTarget(context.Background(), tt.args.target, tt.args.opt...) if tt.wantErr { assert.Error(err) - assert.Nil(target) + assert.Nil(tar) assert.True(errors.Match(errors.T(tt.wantIsError), err)) return } require.NoError(err) - assert.NotNil(target.GetPublicId()) + assert.NotNil(tar.GetPublicId()) hsIds := make([]string, 0, len(hostSources)) for _, s := range hostSources { hsIds = append(hsIds, s.Id()) @@ -196,20 +206,20 @@ func TestRepository_CreateTcpTarget(t *testing.T) { } assert.Equal(tt.wantCredLibs, clIds) - foundTarget, foundHostSources, foundCredLibs, err := repo.LookupTarget(context.Background(), target.GetPublicId()) + foundTarget, foundHostSources, foundCredLibs, err := repo.LookupTarget(context.Background(), tar.GetPublicId()) assert.NoError(err) - assert.True(proto.Equal(target.(*TcpTarget), foundTarget.(*TcpTarget))) + assert.True(proto.Equal(tar.(*tcp.Target), foundTarget.(*tcp.Target))) assert.Equal(hostSources, foundHostSources) assert.Equal(credSources, foundCredLibs) - err = db.TestVerifyOplog(t, rw, target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)) + err = db.TestVerifyOplog(t, rw, tar.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) // TODO (jimlambrt 9/2020) - unfortunately, we can currently // test to make sure that the oplog entry for a target host sets // create exist because the db.TestVerifyOplog doesn't really // support that level of testing and the previous call to - // CreateTcpTarget would create an oplog entry for the + // CreateTarget would create an oplog entry for the // create on the target even if no host sets were added. Once // TestVerifyOplog supports the appropriate granularity, we should // add an appropriate assert. @@ -225,9 +235,9 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { wrapper := db.TestWrapper(t) testKms := kms.TestKms(t, conn, wrapper) - repo, err := NewRepository(rw, rw, testKms) + repo, err := target.NewRepository(rw, rw, testKms) require.NoError(t, err) - id := testId(t) + id := tcp.TestId(t) iamRepo := iam.TestRepo(t, conn, wrapper) _, proj := iam.TestScopes(t, iamRepo) @@ -238,7 +248,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { description string port uint32 fieldMaskPaths []string - opt []Option + opt []target.Option ScopeId string PublicId *string } @@ -246,7 +256,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { name string newScopeId string newName string - newTargetOpts []Option + newTargetOpts []target.Option args args wantRowsUpdate int wantErr bool @@ -288,7 +298,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newScopeId: proj.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "target.(Repository).UpdateTcpTarget: failed for 1: db.DoTx: target.(Repository).UpdateTcpTarget: target.(Repository).update: db.DoTx: target.(Repository).update: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100", + wantErrMsg: "target.(Repository).UpdateTarget: failed for 1: db.DoTx: target.(Repository).UpdateTarget: target.(Repository).update: db.DoTx: target.(Repository).update: db.Update: db.lookupAfterWrite: db.LookupById: record not found, search issue: error #1100", wantIsError: errors.RecordNotFound, }, { @@ -302,7 +312,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newName: "null-name" + id, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "db.DoTx: target.(Repository).UpdateTcpTarget: target.(Repository).update: db.DoTx: target.(Repository).update: db.Update: name must not be empty: not null constraint violated: integrity violation: error #1001", + wantErrMsg: "db.DoTx: target.(Repository).UpdateTarget: target.(Repository).update: db.DoTx: target.(Repository).update: db.Update: name must not be empty: not null constraint violated: integrity violation: error #1001", }, { name: "null-description", @@ -312,7 +322,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { ScopeId: proj.PublicId, }, newScopeId: proj.PublicId, - newTargetOpts: []Option{WithDescription("null-description" + id)}, + newTargetOpts: []target.Option{target.WithDescription("null-description" + id)}, wantErr: false, wantRowsUpdate: 1, }, @@ -326,7 +336,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newScopeId: proj.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "target.(Repository).UpdateTcpTarget: empty field mask: parameter violation: error #104", + wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -339,7 +349,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newScopeId: proj.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "target.(Repository).UpdateTcpTarget: empty field mask: parameter violation: error #104", + wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -352,7 +362,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newScopeId: proj.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "target.(Repository).UpdateTcpTarget: invalid field mask: CreateTime: parameter violation: error #103", + wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: CreateTime: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -365,7 +375,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { newScopeId: proj.PublicId, wantErr: true, wantRowsUpdate: 0, - wantErrMsg: "target.(Repository).UpdateTcpTarget: invalid field mask: Alice: parameter violation: error #103", + wantErrMsg: "target.(Repository).UpdateTarget: invalid field mask: Alice: parameter violation: error #103", wantIsError: errors.InvalidFieldMask, }, { @@ -378,7 +388,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { }, newScopeId: proj.PublicId, wantErr: true, - wantErrMsg: "target.(Repository).UpdateTcpTarget: missing target public id: parameter violation: error #100", + wantErrMsg: "target.(Repository).UpdateTarget: missing target public id: parameter violation: error #100", wantIsError: errors.InvalidParameter, wantRowsUpdate: 0, }, @@ -390,7 +400,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { }, newScopeId: proj.PublicId, wantErr: true, - wantErrMsg: "target.(Repository).UpdateTcpTarget: empty field mask: parameter violation: error #104", + wantErrMsg: "target.(Repository).UpdateTarget: empty field mask: parameter violation: error #104", wantIsError: errors.EmptyFieldMask, }, { @@ -424,7 +434,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { cs := css[i] require, assert := require.New(t), assert.New(t) if tt.wantDup { - _ = TestTcpTarget(t, conn, proj.PublicId, tt.args.name) + _ = tcp.TestTarget(t, conn, proj.PublicId, tt.args.name) } testCats := static.TestCatalogs(t, conn, proj.PublicId, 1) @@ -440,30 +450,31 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { testClIds = append(testClIds, cl.PublicId) } - tt.newTargetOpts = append(tt.newTargetOpts, WithHostSources(testHostSetIds), WithCredentialSources(testClIds)) + tt.newTargetOpts = append(tt.newTargetOpts, target.WithHostSources(testHostSetIds), target.WithCredentialSources(testClIds)) name := tt.newName if name == "" { - name = testId(t) + name = tcp.TestId(t) } - target := TestTcpTarget(t, conn, tt.newScopeId, name, tt.newTargetOpts...) - updateTarget := allocTcpTarget() - updateTarget.PublicId = target.PublicId + tar := tcp.TestTarget(t, conn, tt.newScopeId, name, tt.newTargetOpts...) + updateTarget := tcp.NewTestTarget( + tt.args.ScopeId, + target.WithName(tt.args.name), + target.WithDescription(tt.args.description), + target.WithDefaultPort(tt.args.port), + ) + updateTarget.PublicId = tar.PublicId if tt.args.PublicId != nil { updateTarget.PublicId = *tt.args.PublicId } - updateTarget.ScopeId = tt.args.ScopeId - updateTarget.Name = tt.args.name - updateTarget.Description = tt.args.description - updateTarget.DefaultPort = tt.args.port - targetAfterUpdate, hostSources, credSources, updatedRows, err := repo.UpdateTcpTarget(context.Background(), &updateTarget, target.Version, tt.args.fieldMaskPaths, tt.args.opt...) + targetAfterUpdate, hostSources, credSources, updatedRows, err := repo.UpdateTarget(context.Background(), updateTarget, tar.Version, tt.args.fieldMaskPaths, tt.args.opt...) if tt.wantErr { assert.Error(err) assert.True(errors.Match(errors.T(tt.wantIsError), err)) assert.Nil(targetAfterUpdate) assert.Equal(0, updatedRows) assert.Contains(err.Error(), tt.wantErrMsg) - err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) + err = db.TestVerifyOplog(t, rw, tar.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) assert.Error(err) assert.True(errors.IsNotFoundError(err)) return @@ -485,13 +496,13 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { switch tt.name { case "valid-no-op": - assert.Equal(target.UpdateTime, targetAfterUpdate.(*TcpTarget).UpdateTime) + assert.Equal(tar.UpdateTime, targetAfterUpdate.(*tcp.Target).UpdateTime) default: - assert.NotEqual(target.UpdateTime, targetAfterUpdate.(*TcpTarget).UpdateTime) + assert.NotEqual(tar.UpdateTime, targetAfterUpdate.(*tcp.Target).UpdateTime) } - foundTarget, _, _, err := repo.LookupTarget(context.Background(), target.PublicId) + foundTarget, _, _, err := repo.LookupTarget(context.Background(), tar.PublicId) assert.NoError(err) - assert.True(proto.Equal(targetAfterUpdate.((*TcpTarget)), foundTarget.((*TcpTarget)))) + assert.True(proto.Equal(targetAfterUpdate.((*tcp.Target)), foundTarget.((*tcp.Target)))) underlyingDB, err := conn.SqlDB(ctx) require.NoError(err) dbassert := dbassert.New(t, underlyingDB) @@ -499,7 +510,7 @@ func TestRepository_UpdateTcpTarget(t *testing.T) { assert.Equal(foundTarget.GetDescription(), "") dbassert.IsNull(foundTarget, "description") } - err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) + err = db.TestVerifyOplog(t, rw, tar.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) assert.NoError(err) }) } diff --git a/internal/target/tcp/repository_test.go b/internal/target/tcp/repository_test.go new file mode 100644 index 000000000..32501047b --- /dev/null +++ b/internal/target/tcp/repository_test.go @@ -0,0 +1,347 @@ +package tcp_test + +import ( + "context" + "fmt" + "strconv" + "testing" + "time" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRepository_LookupTarget(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + proj.Name = "project-name" + _, _, err := iamRepo.UpdateScope(context.Background(), proj, 1, []string{"name"}) + require.NoError(t, err) + rw := db.New(conn) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(t, err) + tgt := tcp.TestTarget(t, conn, proj.PublicId, "target-name") + + tests := []struct { + testName string + id string + name string + scopeId string + scopeName string + wantErr bool + }{ + { + testName: "id", + id: tgt.PublicId, + wantErr: false, + }, + { + testName: "name only", + name: tgt.Name, + wantErr: true, + }, + { + testName: "scope id only", + scopeId: proj.PublicId, + wantErr: true, + }, + { + testName: "scope name only", + scopeName: proj.Name, + wantErr: true, + }, + { + testName: "scope name and id", + scopeId: proj.PublicId, + scopeName: proj.Name, + wantErr: true, + }, + { + testName: "everything", + name: tgt.Name, + scopeId: proj.PublicId, + scopeName: proj.Name, + wantErr: true, + }, + { + testName: "name and scope name", + name: tgt.Name, + scopeName: proj.Name, + wantErr: false, + }, + { + testName: "name and scope id", + name: tgt.Name, + scopeId: proj.PublicId, + wantErr: false, + }, + { + testName: "id and name", + id: tgt.PublicId, + name: tgt.Name, + scopeId: proj.PublicId, + wantErr: true, + }, + { + testName: "id and scope name", + id: tgt.PublicId, + scopeName: proj.Name, + wantErr: true, + }, + { + testName: "id and scope id", + id: tgt.PublicId, + scopeId: proj.PublicId, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + id := tt.id + if tt.name != "" && tt.id == "" { + id = tt.name + } + var opts []target.Option + if tt.name != "" { + opts = append(opts, target.WithName(tt.name)) + } + if tt.scopeId != "" { + opts = append(opts, target.WithScopeId(tt.scopeId)) + } + if tt.scopeName != "" { + opts = append(opts, target.WithScopeName(tt.scopeName)) + } + got, _, _, err := repo.LookupTarget(context.Background(), id, opts...) + if tt.wantErr { + require.Error(err) + return + } + require.NoError(err) + assert.Equal(tgt.PublicId, got.GetPublicId()) + }) + } +} + +func TestRepository_ListTargets(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + const testLimit = 10 + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo, err := target.NewRepository(rw, rw, testKms, target.WithLimit(testLimit)) + require.NoError(t, err) + + type args struct { + opt []target.Option + } + tests := []struct { + name string + createCnt int + createScopeId string + createScopeId2 string + grantUserId string + args args + wantCnt int + wantErr bool + }{ + { + name: "tcp-target", + createCnt: 5, + createScopeId: proj.PublicId, + args: args{ + opt: []target.Option{target.WithType(tcp.Subtype), target.WithScopeIds([]string{proj.PublicId})}, + }, + wantCnt: 5, + wantErr: false, + }, + { + name: "no-limit", + createCnt: testLimit + 1, + createScopeId: proj.PublicId, + args: args{ + opt: []target.Option{target.WithLimit(-1), target.WithScopeIds([]string{proj.PublicId})}, + }, + wantCnt: testLimit + 1, + wantErr: false, + }, + { + name: "default-limit", + createCnt: testLimit + 1, + createScopeId: proj.PublicId, + args: args{ + opt: []target.Option{target.WithScopeIds([]string{proj.PublicId})}, + }, + wantCnt: testLimit, + wantErr: false, + }, + { + name: "custom-limit", + createCnt: testLimit + 1, + createScopeId: proj.PublicId, + args: args{ + opt: []target.Option{target.WithLimit(3), target.WithScopeIds([]string{proj.PublicId})}, + }, + wantCnt: 3, + wantErr: false, + }, + { + name: "bad-org", + createCnt: 1, + createScopeId: proj.PublicId, + args: args{ + opt: []target.Option{target.WithScopeIds([]string{"bad-id"})}, + }, + wantCnt: 0, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + require.NoError(conn.Where("1=1").Delete(tcp.NewTestTarget("")).Error) + testGroups := []*tcp.Target{} + for i := 0; i < tt.createCnt; i++ { + switch { + case tt.createScopeId2 != "" && i%2 == 0: + testGroups = append(testGroups, tcp.TestTarget(t, conn, tt.createScopeId2, strconv.Itoa(i))) + default: + testGroups = append(testGroups, tcp.TestTarget(t, conn, tt.createScopeId, strconv.Itoa(i))) + } + } + assert.Equal(tt.createCnt, len(testGroups)) + got, err := repo.ListTargets(context.Background(), tt.args.opt...) + if tt.wantErr { + require.Error(err) + return + } + require.NoError(err) + assert.Equal(tt.wantCnt, len(got)) + }) + } +} + +func TestRepository_ListRoles_Multiple_Scopes(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj1 := iam.TestScopes(t, iamRepo) + _, proj2 := iam.TestScopes(t, iamRepo) + rw := db.New(conn) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(t, err) + + require.NoError(t, conn.Where("1=1").Delete(tcp.NewTestTarget("")).Error) + + const numPerScope = 10 + var total int + for i := 0; i < numPerScope; i++ { + tcp.TestTarget(t, conn, proj1.GetPublicId(), fmt.Sprintf("proj1-%d", i)) + total++ + tcp.TestTarget(t, conn, proj2.GetPublicId(), fmt.Sprintf("proj2-%d", i)) + total++ + } + + got, err := repo.ListTargets(context.Background(), target.WithScopeIds([]string{"global", proj1.GetPublicId(), proj2.GetPublicId()})) + require.NoError(t, err) + assert.Equal(t, total, len(got)) +} + +func TestRepository_DeleteTarget(t *testing.T) { + t.Parallel() + conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) + wrapper := db.TestWrapper(t) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(t, err) + + type args struct { + target target.Target + opt []target.Option + } + tests := []struct { + name string + args args + wantRowsDeleted int + wantErr bool + wantErrMsg string + }{ + { + name: "valid", + args: args{ + target: tcp.TestTarget(t, conn, proj.PublicId, "valid"), + }, + wantRowsDeleted: 1, + wantErr: false, + }, + { + name: "no-public-id", + args: args{ + target: func() target.Target { + tar, _ := tcp.New(proj.PublicId) + return tar + }(), + }, + wantRowsDeleted: 0, + wantErr: true, + wantErrMsg: "target.(Repository).DeleteTarget: missing public id: parameter violation: error #100", + }, + { + name: "not-found", + args: args{ + target: func() target.Target { + id, err := db.NewPublicId(tcp.TargetPrefix) + require.NoError(t, err) + tar, _ := tcp.New(proj.PublicId) + tar.PublicId = id + return tar + }(), + }, + wantRowsDeleted: 0, + wantErr: true, + wantErrMsg: "db.LookupById: record not found, search issue: error #1100", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert := assert.New(t) + deletedRows, err := repo.DeleteTarget(context.Background(), tt.args.target.GetPublicId(), tt.args.opt...) + if tt.wantErr { + assert.Error(err) + assert.Equal(0, deletedRows) + assert.Contains(err.Error(), tt.wantErrMsg) + err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) + assert.Error(err) + assert.True(errors.IsNotFoundError(err)) + return + } + assert.NoError(err) + assert.Equal(tt.wantRowsDeleted, deletedRows) + foundGroup, _, _, err := repo.LookupTarget(context.Background(), tt.args.target.GetPublicId()) + assert.NoError(err) + assert.Nil(foundGroup) + + err = db.TestVerifyOplog(t, rw, tt.args.target.GetPublicId(), db.WithOperation(oplog.OpType_OP_TYPE_DELETE), db.WithCreateNotBefore(10*time.Second)) + assert.NoError(err) + }) + } +} diff --git a/internal/target/tcp/store/target.pb.go b/internal/target/tcp/store/target.pb.go new file mode 100644 index 000000000..903fe7f15 --- /dev/null +++ b/internal/target/tcp/store/target.pb.go @@ -0,0 +1,306 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.17.3 +// source: controller/storage/target/tcp/store/v1/target.proto + +package store + +import ( + timestamp "github.com/hashicorp/boundary/internal/db/timestamp" + _ "github.com/hashicorp/boundary/sdk/pbs/controller/protooptions" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Target struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // public_id is used to access the tcp.Target via an API + // @inject_tag: gorm:"primary_key" + PublicId string `protobuf:"bytes,10,opt,name=public_id,json=publicId,proto3" json:"public_id,omitempty" gorm:"primary_key"` + // scope id for the tcp.Target + // @inject_tag: `gorm:"default:null"` + ScopeId string `protobuf:"bytes,20,opt,name=scope_id,json=scopeId,proto3" json:"scope_id,omitempty" gorm:"default:null"` + // name is the optional friendly name used to + // access the tcp.Target via an API + // @inject_tag: `gorm:"default:null"` + Name string `protobuf:"bytes,30,opt,name=name,proto3" json:"name,omitempty" gorm:"default:null"` + // description of the tcp.Target + // @inject_tag: `gorm:"default:null"` + Description string `protobuf:"bytes,40,opt,name=description,proto3" json:"description,omitempty" gorm:"default:null"` + // create_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + CreateTime *timestamp.Timestamp `protobuf:"bytes,50,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty" gorm:"default:current_timestamp"` + // update_time from the RDBMS + // @inject_tag: `gorm:"default:current_timestamp"` + UpdateTime *timestamp.Timestamp `protobuf:"bytes,60,opt,name=update_time,json=updateTime,proto3" json:"update_time,omitempty" gorm:"default:current_timestamp"` + // version allows optimistic locking of the tcp.Target when modifying the + // tcp.Target + // @inject_tag: `gorm:"default:null"` + Version uint32 `protobuf:"varint,70,opt,name=version,proto3" json:"version,omitempty" gorm:"default:null"` + // default port of the tcp.Target + // @inject_tag: `gorm:"default:null"` + DefaultPort uint32 `protobuf:"varint,80,opt,name=default_port,json=defaultPort,proto3" json:"default_port,omitempty" gorm:"default:null"` + // Maximum total lifetime of a created session, in seconds + // @inject_tag: `gorm:"default:null"` + SessionMaxSeconds uint32 `protobuf:"varint,100,opt,name=session_max_seconds,json=sessionMaxSeconds,proto3" json:"session_max_seconds,omitempty" gorm:"default:null"` + // Maximum number of connections in a session + // @inject_tag: `gorm:"default:null"` + SessionConnectionLimit int32 `protobuf:"varint,110,opt,name=session_connection_limit,json=sessionConnectionLimit,proto3" json:"session_connection_limit,omitempty" gorm:"default:null"` + // A boolean expression that allows filtering the workers that can handle a session + // @inject_tag: `gorm:"default:null"` + WorkerFilter string `protobuf:"bytes,120,opt,name=worker_filter,json=workerFilter,proto3" json:"worker_filter,omitempty" gorm:"default:null"` +} + +func (x *Target) Reset() { + *x = Target{} + if protoimpl.UnsafeEnabled { + mi := &file_controller_storage_target_tcp_store_v1_target_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Target) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Target) ProtoMessage() {} + +func (x *Target) ProtoReflect() protoreflect.Message { + mi := &file_controller_storage_target_tcp_store_v1_target_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Target.ProtoReflect.Descriptor instead. +func (*Target) Descriptor() ([]byte, []int) { + return file_controller_storage_target_tcp_store_v1_target_proto_rawDescGZIP(), []int{0} +} + +func (x *Target) GetPublicId() string { + if x != nil { + return x.PublicId + } + return "" +} + +func (x *Target) GetScopeId() string { + if x != nil { + return x.ScopeId + } + return "" +} + +func (x *Target) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Target) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Target) GetCreateTime() *timestamp.Timestamp { + if x != nil { + return x.CreateTime + } + return nil +} + +func (x *Target) GetUpdateTime() *timestamp.Timestamp { + if x != nil { + return x.UpdateTime + } + return nil +} + +func (x *Target) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *Target) GetDefaultPort() uint32 { + if x != nil { + return x.DefaultPort + } + return 0 +} + +func (x *Target) GetSessionMaxSeconds() uint32 { + if x != nil { + return x.SessionMaxSeconds + } + return 0 +} + +func (x *Target) GetSessionConnectionLimit() int32 { + if x != nil { + return x.SessionConnectionLimit + } + return 0 +} + +func (x *Target) GetWorkerFilter() string { + if x != nil { + return x.WorkerFilter + } + return "" +} + +var File_controller_storage_target_tcp_store_v1_target_proto protoreflect.FileDescriptor + +var file_controller_storage_target_tcp_store_v1_target_proto_rawDesc = []byte{ + 0x0a, 0x33, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2f, 0x74, 0x63, 0x70, 0x2f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x26, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x2e, 0x74, 0x63, 0x70, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x76, 0x31, 0x1a, 0x2f, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2f, 0x76, 0x31, 0x2f, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x63, 0x75, 0x73, 0x74, 0x6f, + 0x6d, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc3, 0x05, 0x0a, 0x06, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 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, 0x19, 0x0a, 0x08, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x14, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x49, 0x64, 0x12, 0x24, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x09, 0x42, 0x10, 0xc2, 0xdd, 0x29, + 0x0c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x40, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x28, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1e, 0xc2, 0xdd, 0x29, 0x1a, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x32, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, + 0x6d, 0x65, 0x12, 0x4b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x3c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x6c, 0x65, 0x72, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x0c, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x50, 0x20, 0x01, 0x28, 0x0d, 0x42, + 0x2a, 0xc2, 0xdd, 0x29, 0x26, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, + 0x72, 0x74, 0x12, 0x17, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, + 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0b, 0x64, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x5c, 0x0a, 0x13, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, + 0x64, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x2c, 0xc2, 0xdd, 0x29, 0x28, 0x0a, 0x11, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x13, + 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x52, 0x11, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x78, 0x53, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x70, 0x0a, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x05, 0x42, 0x36, 0xc2, 0xdd, 0x29, 0x32, 0x0a, 0x16, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x52, 0x16, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x46, 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, + 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x78, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x21, 0xc2, 0xdd, 0x29, 0x1d, 0x0a, 0x0c, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x42, 0x3f, 0x5a, 0x3d, 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, 0x74, 0x63, 0x70, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x3b, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_controller_storage_target_tcp_store_v1_target_proto_rawDescOnce sync.Once + file_controller_storage_target_tcp_store_v1_target_proto_rawDescData = file_controller_storage_target_tcp_store_v1_target_proto_rawDesc +) + +func file_controller_storage_target_tcp_store_v1_target_proto_rawDescGZIP() []byte { + file_controller_storage_target_tcp_store_v1_target_proto_rawDescOnce.Do(func() { + file_controller_storage_target_tcp_store_v1_target_proto_rawDescData = protoimpl.X.CompressGZIP(file_controller_storage_target_tcp_store_v1_target_proto_rawDescData) + }) + return file_controller_storage_target_tcp_store_v1_target_proto_rawDescData +} + +var file_controller_storage_target_tcp_store_v1_target_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_controller_storage_target_tcp_store_v1_target_proto_goTypes = []interface{}{ + (*Target)(nil), // 0: controller.storage.target.tcp.store.v1.Target + (*timestamp.Timestamp)(nil), // 1: controller.storage.timestamp.v1.Timestamp +} +var file_controller_storage_target_tcp_store_v1_target_proto_depIdxs = []int32{ + 1, // 0: controller.storage.target.tcp.store.v1.Target.create_time:type_name -> controller.storage.timestamp.v1.Timestamp + 1, // 1: controller.storage.target.tcp.store.v1.Target.update_time:type_name -> controller.storage.timestamp.v1.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_controller_storage_target_tcp_store_v1_target_proto_init() } +func file_controller_storage_target_tcp_store_v1_target_proto_init() { + if File_controller_storage_target_tcp_store_v1_target_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_controller_storage_target_tcp_store_v1_target_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Target); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_controller_storage_target_tcp_store_v1_target_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_controller_storage_target_tcp_store_v1_target_proto_goTypes, + DependencyIndexes: file_controller_storage_target_tcp_store_v1_target_proto_depIdxs, + MessageInfos: file_controller_storage_target_tcp_store_v1_target_proto_msgTypes, + }.Build() + File_controller_storage_target_tcp_store_v1_target_proto = out.File + file_controller_storage_target_tcp_store_v1_target_proto_rawDesc = nil + file_controller_storage_target_tcp_store_v1_target_proto_goTypes = nil + file_controller_storage_target_tcp_store_v1_target_proto_depIdxs = nil +} diff --git a/internal/target/tcp/target.go b/internal/target/tcp/target.go new file mode 100644 index 000000000..e98a7f329 --- /dev/null +++ b/internal/target/tcp/target.go @@ -0,0 +1,173 @@ +// Package tcp provides a Target subtype for a TCP Target. +// Importing this package will register it with the target package and +// allow the target.Repository to support tcp.Targets. +package tcp + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/db/timestamp" + "github.com/hashicorp/boundary/internal/errors" + "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp/store" + "github.com/hashicorp/boundary/internal/types/subtypes" + "google.golang.org/protobuf/proto" +) + +const ( + defaultTableName = "target_tcp" + Subtype = subtypes.Subtype("tcp") +) + +// Target is a resources that represets a networked service +// that can be accessed via TCP. It is a subtype of target.Target. +type Target struct { + *store.Target + tableName string `gorm:"-"` +} + +// Ensure Target implements interfaces +var ( + _ target.Target = (*Target)(nil) + _ db.VetForWriter = (*Target)(nil) + _ oplog.ReplayableMessage = (*Target)(nil) +) + +// New creates a new in memory tcp target. WithName, WithDescription and +// WithDefaultPort options are supported +func New(scopeId string, opt ...target.Option) (*Target, error) { + const op = "tcp.NewTarget" + opts := target.GetOpts(opt...) + if scopeId == "" { + return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing scope id") + } + t := &Target{ + Target: &store.Target{ + ScopeId: scopeId, + Name: opts.WithName, + Description: opts.WithDescription, + DefaultPort: opts.WithDefaultPort, + SessionConnectionLimit: opts.WithSessionConnectionLimit, + SessionMaxSeconds: opts.WithSessionMaxSeconds, + WorkerFilter: opts.WithWorkerFilter, + }, + } + return t, nil +} + +// allocTarget will allocate a tcp target +func allocTarget() target.Target { + return &Target{ + Target: &store.Target{}, + } +} + +// Clone creates a clone of the Target +func (t *Target) Clone() target.Target { + cp := proto.Clone(t.Target) + return &Target{ + Target: cp.(*store.Target), + } +} + +// VetForWrite implements db.VetForWrite() interface and validates the tcp target +// before it's written. +func (t *Target) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error { + const op = "tcp.(Target).VetForWrite" + if t.PublicId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing public id") + } + if opType == db.CreateOp { + if t.ScopeId == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing scope id") + } + if t.Name == "" { + return errors.New(ctx, errors.InvalidParameter, op, "missing name") + } + } + return nil +} + +// TableName returns the tablename to override the default gorm table name +func (t *Target) TableName() string { + if t.tableName != "" { + return t.tableName + } + return defaultTableName +} + +// SetTableName sets the tablename and satisfies the ReplayableMessage +// interface. If the caller attempts to set the name to "" the name will be +// reset to the default name. +func (t *Target) SetTableName(n string) { + t.tableName = n +} + +// Oplog provides the oplog.Metadata for recording operations taken on a Target. +func (t *Target) Oplog(op oplog.OpType) oplog.Metadata { + metadata := oplog.Metadata{ + "resource-public-id": []string{t.PublicId}, + "resource-type": []string{"tcp target"}, + "op-type": []string{op.String()}, + "scope-id": []string{t.ScopeId}, + } + return metadata +} + +func (t *Target) GetType() subtypes.Subtype { + return Subtype +} + +func (t *Target) SetPublicId(ctx context.Context, publicId string) error { + const op = "tcp.(Target).SetPublicId" + if !strings.HasPrefix(publicId, TargetPrefix+"_") { + return errors.New(ctx, errors.InvalidParameter, op, fmt.Sprintf("passed-in public ID %q has wrong prefix, should be %q", publicId, TargetPrefix)) + } + + t.PublicId = publicId + return nil +} + +func (t *Target) SetScopeId(scopeId string) { + t.ScopeId = scopeId +} + +func (t *Target) SetName(name string) { + t.Name = name +} + +func (t *Target) SetDescription(description string) { + t.Description = description +} + +func (t *Target) SetVersion(v uint32) { + t.Version = v +} + +func (t *Target) SetDefaultPort(port uint32) { + t.DefaultPort = port +} + +func (t *Target) SetCreateTime(ts *timestamp.Timestamp) { + t.CreateTime = ts +} + +func (t *Target) SetUpdateTime(ts *timestamp.Timestamp) { + t.UpdateTime = ts +} + +func (t *Target) SetSessionMaxSeconds(s uint32) { + t.SessionMaxSeconds = s +} + +func (t *Target) SetSessionConnectionLimit(limit int32) { + t.SessionConnectionLimit = limit +} + +func (t *Target) SetWorkerFilter(filter string) { + t.WorkerFilter = filter +} diff --git a/internal/target/tcp_target_test.go b/internal/target/tcp/target_test.go similarity index 71% rename from internal/target/tcp_target_test.go rename to internal/target/tcp/target_test.go index faa059033..82ec6b46c 100644 --- a/internal/target/tcp_target_test.go +++ b/internal/target/tcp/target_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -10,24 +10,26 @@ import ( "github.com/hashicorp/boundary/internal/errors" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/oplog" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) -func TestTcpTarget_Create(t *testing.T) { +func TestTarget_Create(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) _, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) type args struct { scopeId string - opt []Option + opt []target.Option } tests := []struct { name string args args - want *TcpTarget + want *tcp.Target wantErr bool wantIsErr errors.Code create bool @@ -43,15 +45,16 @@ func TestTcpTarget_Create(t *testing.T) { name: "valid-proj-scope", args: args{ scopeId: prj.PublicId, - opt: []Option{WithName("valid-proj-scope")}, + opt: []target.Option{target.WithName("valid-proj-scope")}, }, - want: func() *TcpTarget { - t := allocTcpTarget() - t.ScopeId = prj.PublicId - t.Name = "valid-proj-scope" - t.SessionMaxSeconds = uint32((8 * time.Hour).Seconds()) - t.SessionConnectionLimit = 1 - return &t + want: func() *tcp.Target { + t, _ := tcp.New( + prj.PublicId, + target.WithName("valid-proj-scope"), + target.WithSessionMaxSeconds(uint32((8 * time.Hour).Seconds())), + target.WithSessionConnectionLimit(1), + ) + return t }(), create: true, }, @@ -59,7 +62,7 @@ func TestTcpTarget_Create(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - got, err := NewTcpTarget(tt.args.scopeId, tt.args.opt...) + got, err := tcp.New(tt.args.scopeId, tt.args.opt...) if tt.wantErr { require.Error(err) assert.True(errors.Match(errors.T(tt.wantIsErr), err)) @@ -68,22 +71,22 @@ func TestTcpTarget_Create(t *testing.T) { require.NoError(err) assert.Equal(tt.want, got) if tt.create { - id, err := newTcpTargetId() + id, err := db.NewPublicId(tcp.TargetPrefix) require.NoError(err) got.PublicId = id err = db.New(conn).Create(context.Background(), got) if tt.wantCreateErr { assert.Error(err) return - } else { - assert.NoError(err) } + + assert.NoError(err) } }) } } -func TestTcpTarget_Delete(t *testing.T) { +func TestTarget_Delete(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) @@ -92,27 +95,27 @@ func TestTcpTarget_Delete(t *testing.T) { tests := []struct { name string - target *TcpTarget + target *tcp.Target wantRowsDeleted int wantErr bool wantErrMsg string }{ { name: "valid", - target: TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId)), + target: tcp.TestTarget(t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId)), wantErr: false, wantRowsDeleted: 1, }, { name: "bad-id", - target: func() *TcpTarget { - target := allocTcpTarget() - id, err := newTcpTargetId() + target: func() *tcp.Target { + tar, _ := tcp.New(proj.PublicId) + + id, err := db.NewPublicId(tcp.TargetPrefix) require.NoError(t, err) - target.PublicId = id - target.ScopeId = proj.PublicId - target.Name = testTargetName(t, proj.PublicId) - return &target + tar.PublicId = id + tar.Name = tcp.TestTargetName(t, proj.PublicId) + return tar }(), wantErr: false, wantRowsDeleted: 0, @@ -121,9 +124,9 @@ func TestTcpTarget_Delete(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - deleteTarget := allocTcpTarget() + deleteTarget := tcp.NewTestTarget("") deleteTarget.PublicId = tt.target.PublicId - deletedRows, err := rw.Delete(context.Background(), &deleteTarget) + deletedRows, err := rw.Delete(context.Background(), deleteTarget) if tt.wantErr { require.Error(err) return @@ -134,18 +137,18 @@ func TestTcpTarget_Delete(t *testing.T) { return } assert.Equal(tt.wantRowsDeleted, deletedRows) - foundTarget := allocTcpTarget() + foundTarget := tcp.NewTestTarget("") foundTarget.PublicId = tt.target.PublicId - err = rw.LookupById(context.Background(), &foundTarget) + err = rw.LookupById(context.Background(), foundTarget) require.Error(err) assert.True(errors.IsNotFoundError(err)) }) } } -func TestTcpTarget_Update(t *testing.T) { +func TestTarget_Update(t *testing.T) { t.Parallel() - id := testId(t) + id := tcp.TestId(t) ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) @@ -246,37 +249,36 @@ func TestTcpTarget_Update(t *testing.T) { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) if tt.wantDup { - target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId)) + target := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId)) target.Name = tt.args.name _, err := rw.Update(context.Background(), target, tt.args.fieldMaskPaths, tt.args.nullPaths) require.NoError(err) } - id := testId(t) - target := TestTcpTarget(t, conn, proj.PublicId, id, WithDescription(id)) + id := tcp.TestId(t) + tar := tcp.TestTarget(t, conn, proj.PublicId, id, target.WithDescription(id)) - updateTarget := allocTcpTarget() - updateTarget.PublicId = target.PublicId - updateTarget.ScopeId = tt.args.ScopeId + updateTarget := tcp.NewTestTarget(tt.args.ScopeId) + updateTarget.PublicId = tar.PublicId updateTarget.Name = tt.args.name updateTarget.Description = tt.args.description - updatedRows, err := rw.Update(context.Background(), &updateTarget, tt.args.fieldMaskPaths, tt.args.nullPaths) + updatedRows, err := rw.Update(context.Background(), updateTarget, tt.args.fieldMaskPaths, tt.args.nullPaths) if tt.wantErr { require.Error(err) assert.Equal(0, updatedRows) assert.Equal(tt.wantErrMsg, err.Error()) - err = db.TestVerifyOplog(t, rw, target.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) + err = db.TestVerifyOplog(t, rw, tar.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_UPDATE), db.WithCreateNotBefore(10*time.Second)) require.Error(err) assert.Contains(err.Error(), "record not found") return } require.NoError(err) assert.Equal(tt.wantRowsUpdate, updatedRows) - assert.NotEqual(target.UpdateTime, updateTarget.UpdateTime) - foundTarget := allocTcpTarget() - foundTarget.PublicId = target.GetPublicId() - err = rw.LookupByPublicId(context.Background(), &foundTarget) + assert.NotEqual(tar.UpdateTime, updateTarget.UpdateTime) + foundTarget := tcp.NewTestTarget(tt.args.ScopeId) + foundTarget.PublicId = tar.GetPublicId() + err = rw.LookupByPublicId(context.Background(), foundTarget) require.NoError(err) assert.True(proto.Equal(updateTarget, foundTarget)) if len(tt.args.nullPaths) != 0 { @@ -291,49 +293,49 @@ func TestTcpTarget_Update(t *testing.T) { } t.Run("update dup names in diff scopes", func(t *testing.T) { assert, require := assert.New(t), require.New(t) - id := testId(t) + id := tcp.TestId(t) _, proj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - _ = TestTcpTarget(t, conn, proj2.PublicId, id, WithDescription(id)) - projTarget := TestTcpTarget(t, conn, proj.PublicId, id) + _ = tcp.TestTarget(t, conn, proj2.PublicId, id, target.WithDescription(id)) + projTarget := tcp.TestTarget(t, conn, proj.PublicId, id) projTarget.Name = id updatedRows, err := rw.Update(context.Background(), projTarget, []string{"Name"}, nil) require.NoError(err) assert.Equal(1, updatedRows) - foundTarget := allocTcpTarget() + foundTarget, _ := tcp.New(proj2.PublicId) foundTarget.PublicId = projTarget.GetPublicId() - err = rw.LookupByPublicId(context.Background(), &foundTarget) + err = rw.LookupByPublicId(context.Background(), foundTarget) require.NoError(err) assert.Equal(id, projTarget.Name) }) } -func TestTcpTarget_Clone(t *testing.T) { +func TestTarget_Clone(t *testing.T) { t.Parallel() conn, _ := db.TestSetup(t, "postgres") wrapper := db.TestWrapper(t) t.Run("valid", func(t *testing.T) { assert := assert.New(t) _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId)) - cp := target.Clone() - assert.True(proto.Equal(cp.(*TcpTarget).TcpTarget, target.TcpTarget)) + tar := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId)) + cp := tar.Clone() + assert.True(proto.Equal(cp.(*tcp.Target).Target, tar.Target)) }) t.Run("not-equal", func(t *testing.T) { assert := assert.New(t) _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) _, proj2 := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) - target := TestTcpTarget(t, conn, proj.PublicId, testTargetName(t, proj.PublicId)) - target2 := TestTcpTarget(t, conn, proj2.PublicId, testTargetName(t, proj2.PublicId)) + target1 := tcp.TestTarget(t, conn, proj.PublicId, tcp.TestTargetName(t, proj.PublicId)) + target2 := tcp.TestTarget(t, conn, proj2.PublicId, tcp.TestTargetName(t, proj2.PublicId)) - cp := target.Clone() - assert.True(!proto.Equal(cp.(*TcpTarget).TcpTarget, target2.TcpTarget)) + cp := target1.Clone() + assert.True(!proto.Equal(cp.(*tcp.Target).Target, target2.Target)) }) } -func TestTcpTable_SetTableName(t *testing.T) { +func TestTable_SetTableName(t *testing.T) { t.Parallel() - defaultTableName := DefaultTcpTableName + defaultTableName := tcp.DefaultTableName tests := []struct { name string setNameTo string @@ -353,30 +355,29 @@ func TestTcpTable_SetTableName(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - def := allocTcpTarget() + def, _ := tcp.New("testScope") require.Equal(defaultTableName, def.TableName()) - s := allocTcpTarget() + s, _ := tcp.New("testScope") s.SetTableName(tt.setNameTo) assert.Equal(tt.want, s.TableName()) }) } } -func TestTcpTarget_oplog(t *testing.T) { - id := testId(t) +func TestTarget_oplog(t *testing.T) { + id := tcp.TestId(t) tests := []struct { name string - target *TcpTarget + target *tcp.Target op oplog.OpType want oplog.Metadata }{ { name: "simple", - target: func() *TcpTarget { - t := allocTcpTarget() + target: func() *tcp.Target { + t, _ := tcp.New(id) t.PublicId = id - t.ScopeId = id - return &t + return t }(), op: oplog.OpType_OP_TYPE_CREATE, want: oplog.Metadata{ @@ -390,7 +391,7 @@ func TestTcpTarget_oplog(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert := assert.New(t) - got := tt.target.oplog(tt.op) + got := tt.target.Oplog(tt.op) assert.Equal(got, tt.want) }) } diff --git a/internal/target/tcp/testing.go b/internal/target/tcp/testing.go new file mode 100644 index 000000000..f78f61e9b --- /dev/null +++ b/internal/target/tcp/testing.go @@ -0,0 +1,62 @@ +package tcp + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/go-uuid" + "github.com/stretchr/testify/require" +) + +// TestTarget is used to create a Target that can be used by tests in other packages. +func TestTarget(t *testing.T, conn *db.DB, scopeId, name string, opt ...target.Option) *Target { + t.Helper() + opt = append(opt, target.WithName(name)) + opts := target.GetOpts(opt...) + require := require.New(t) + rw := db.New(conn) + tar, err := New(scopeId, opt...) + require.NoError(err) + id, err := db.NewPublicId(TargetPrefix) + require.NoError(err) + tar.PublicId = id + err = rw.Create(context.Background(), tar) + require.NoError(err) + + if len(opts.WithHostSources) > 0 { + newHostSets := make([]interface{}, 0, len(opts.WithHostSources)) + for _, s := range opts.WithHostSources { + hostSet, err := target.NewTargetHostSet(tar.PublicId, s) + require.NoError(err) + newHostSets = append(newHostSets, hostSet) + } + err := rw.CreateItems(context.Background(), newHostSets) + require.NoError(err) + } + if len(opts.WithCredentialSources) > 0 { + newCredLibs := make([]interface{}, 0, len(opts.WithCredentialSources)) + for _, cl := range opts.WithCredentialSources { + newCl, err := target.NewCredentialLibrary(tar.PublicId, cl) + require.NoError(err) + newCredLibs = append(newCredLibs, newCl) + } + err := rw.CreateItems(context.Background(), newCredLibs) + require.NoError(err) + } + return tar +} + +func testTargetName(t *testing.T, scopeId string) string { + t.Helper() + return fmt.Sprintf("%s-%s", scopeId, testId(t)) +} + +func testId(t *testing.T) string { + t.Helper() + id, err := uuid.GenerateUUID() + require.NoError(t, err) + return id +} diff --git a/internal/target/testing_test.go b/internal/target/tcp/testing_test.go similarity index 58% rename from internal/target/testing_test.go rename to internal/target/tcp/testing_test.go index 1d8c53863..7faeb2c14 100644 --- a/internal/target/testing_test.go +++ b/internal/target/tcp/testing_test.go @@ -1,4 +1,4 @@ -package target +package tcp_test import ( "context" @@ -8,6 +8,9 @@ import ( "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" + "github.com/hashicorp/boundary/internal/kms" + "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -15,22 +18,27 @@ import ( func Test_TestTcpTarget(t *testing.T) { require := require.New(t) conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) wrapper := db.TestWrapper(t) - _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(err) + cats := static.TestCatalogs(t, conn, proj.PublicId, 1) hsets := static.TestSets(t, conn, cats[0].GetPublicId(), 2) var sets []string for _, s := range hsets { sets = append(sets, s.PublicId) } - name := testTargetName(t, proj.PublicId) - target := TestTcpTarget(t, conn, proj.PublicId, name, WithHostSources(sets)) + name := tcp.TestTargetName(t, proj.PublicId) + tar := tcp.TestTarget(t, conn, proj.PublicId, name, target.WithHostSources(sets)) require.NotNil(t) - require.NotEmpty(target.PublicId) - require.Equal(name, target.Name) + require.NotEmpty(tar.PublicId) + require.Equal(name, tar.Name) - rw := db.New(conn) - foundSources, err := fetchHostSources(context.Background(), rw, target.PublicId) + _, foundSources, _, err := repo.LookupTarget(context.Background(), tar.PublicId) require.NoError(err) foundIds := make([]string, 0, len(foundSources)) for _, s := range foundSources { @@ -42,25 +50,29 @@ func Test_TestTcpTarget(t *testing.T) { func Test_TestCredentialLibrary(t *testing.T) { assert, require := assert.New(t), require.New(t) conn, _ := db.TestSetup(t, "postgres") + rw := db.New(conn) wrapper := db.TestWrapper(t) - _, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper)) + testKms := kms.TestKms(t, conn, wrapper) + iamRepo := iam.TestRepo(t, conn, wrapper) + _, proj := iam.TestScopes(t, iamRepo) + repo, err := target.NewRepository(rw, rw, testKms) + require.NoError(err) - target := TestTcpTarget(t, conn, proj.PublicId, t.Name()) + tar := tcp.TestTarget(t, conn, proj.PublicId, t.Name()) store := vault.TestCredentialStores(t, conn, wrapper, proj.GetPublicId(), 1)[0] vlibs := vault.TestCredentialLibraries(t, conn, wrapper, store.GetPublicId(), 2) var libIds []string - var libs []*CredentialLibrary + var libs []*target.CredentialLibrary for _, v := range vlibs { libIds = append(libIds, v.GetPublicId()) - lib := TestCredentialLibrary(t, conn, target.GetPublicId(), v.GetPublicId()) + lib := target.TestCredentialLibrary(t, conn, tar.GetPublicId(), v.GetPublicId()) require.NotNil(lib) libs = append(libs, lib) } assert.Len(libs, 2) - rw := db.New(conn) - foundSources, err := fetchCredentialSources(context.Background(), rw, target.PublicId) + _, _, foundSources, err := repo.LookupTarget(context.Background(), tar.PublicId) require.NoError(err) foundIds := make([]string, 0, len(foundSources)) for _, s := range foundSources { diff --git a/internal/target/tcp_target.go b/internal/target/tcp_target.go deleted file mode 100644 index b43d94aa3..000000000 --- a/internal/target/tcp_target.go +++ /dev/null @@ -1,110 +0,0 @@ -package target - -import ( - "context" - - "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/boundary/internal/errors" - "github.com/hashicorp/boundary/internal/oplog" - "github.com/hashicorp/boundary/internal/target/store" - "google.golang.org/protobuf/proto" -) - -const ( - DefaultTcpTableName = "target_tcp" -) - -type TcpTarget struct { - *store.TcpTarget - tableName string `gorm:"-"` -} - -var ( - _ Target = (*TcpTarget)(nil) - _ db.VetForWriter = (*TcpTarget)(nil) - _ oplog.ReplayableMessage = (*TcpTarget)(nil) -) - -// NewTcpTarget creates a new in memory tcp target. WithName, WithDescription and -// WithDefaultPort options are supported -func NewTcpTarget(scopeId string, opt ...Option) (*TcpTarget, error) { - const op = "target.NewTcpTarget" - opts := getOpts(opt...) - if scopeId == "" { - return nil, errors.NewDeprecated(errors.InvalidParameter, op, "missing scope id") - } - t := &TcpTarget{ - TcpTarget: &store.TcpTarget{ - ScopeId: scopeId, - Name: opts.withName, - Description: opts.withDescription, - DefaultPort: opts.withDefaultPort, - SessionConnectionLimit: opts.withSessionConnectionLimit, - SessionMaxSeconds: opts.withSessionMaxSeconds, - WorkerFilter: opts.withWorkerFilter, - }, - } - return t, nil -} - -// allocTcpTarget will allocate a tcp target -func allocTcpTarget() TcpTarget { - return TcpTarget{ - TcpTarget: &store.TcpTarget{}, - } -} - -// Clone creates a clone of the TcpTarget -func (t *TcpTarget) Clone() interface{} { - cp := proto.Clone(t.TcpTarget) - return &TcpTarget{ - TcpTarget: cp.(*store.TcpTarget), - } -} - -// VetForWrite implements db.VetForWrite() interface and validates the tcp target -// before it's written. -func (t *TcpTarget) VetForWrite(ctx context.Context, _ db.Reader, opType db.OpType, _ ...db.Option) error { - const op = "target.(TcpTarget).VetForWrite" - if t.PublicId == "" { - return errors.New(ctx, errors.InvalidParameter, op, "missing public id") - } - if opType == db.CreateOp { - if t.ScopeId == "" { - return errors.New(ctx, errors.InvalidParameter, op, "missing scope id") - } - if t.Name == "" { - return errors.New(ctx, errors.InvalidParameter, op, "missing name") - } - } - return nil -} - -// TableName returns the tablename to override the default gorm table name -func (t *TcpTarget) TableName() string { - if t.tableName != "" { - return t.tableName - } - return DefaultTcpTableName -} - -// SetTableName sets the tablename and satisfies the ReplayableMessage -// interface. If the caller attempts to set the name to "" the name will be -// reset to the default name. -func (t *TcpTarget) SetTableName(n string) { - t.tableName = n -} - -func (t *TcpTarget) oplog(op oplog.OpType) oplog.Metadata { - metadata := oplog.Metadata{ - "resource-public-id": []string{t.PublicId}, - "resource-type": []string{"tcp target"}, - "op-type": []string{op.String()}, - "scope-id": []string{t.ScopeId}, - } - return metadata -} - -func (t TcpTarget) GetType() string { - return "tcp" -} diff --git a/internal/target/testing.go b/internal/target/testing.go index a1761a659..b436a7963 100644 --- a/internal/target/testing.go +++ b/internal/target/testing.go @@ -2,63 +2,12 @@ package target import ( "context" - "fmt" "testing" "github.com/hashicorp/boundary/internal/db" - "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" ) -func TestTcpTarget(t *testing.T, conn *db.DB, scopeId, name string, opt ...Option) *TcpTarget { - t.Helper() - opt = append(opt, WithName(name)) - opts := getOpts(opt...) - require := require.New(t) - rw := db.New(conn) - target, err := NewTcpTarget(scopeId, opt...) - require.NoError(err) - id, err := newTcpTargetId() - require.NoError(err) - target.PublicId = id - err = rw.Create(context.Background(), target) - require.NoError(err) - - if len(opts.withHostSources) > 0 { - newHostSets := make([]interface{}, 0, len(opts.withHostSources)) - for _, s := range opts.withHostSources { - hostSet, err := NewTargetHostSet(target.PublicId, s) - require.NoError(err) - newHostSets = append(newHostSets, hostSet) - } - err := rw.CreateItems(context.Background(), newHostSets) - require.NoError(err) - } - if len(opts.withCredentialSources) > 0 { - newCredLibs := make([]interface{}, 0, len(opts.withCredentialSources)) - for _, cl := range opts.withCredentialSources { - newCl, err := NewCredentialLibrary(target.PublicId, cl) - require.NoError(err) - newCredLibs = append(newCredLibs, newCl) - } - err := rw.CreateItems(context.Background(), newCredLibs) - require.NoError(err) - } - return target -} - -func testTargetName(t *testing.T, scopeId string) string { - t.Helper() - return fmt.Sprintf("%s-%s", scopeId, testId(t)) -} - -func testId(t *testing.T) string { - t.Helper() - id, err := uuid.GenerateUUID() - require.NoError(t, err) - return id -} - // TestCredentialLibrary creates a CredentialLibrary for targetId and // libraryId. func TestCredentialLibrary(t *testing.T, conn *db.DB, targetId, libraryId string) *CredentialLibrary { diff --git a/internal/tests/api/targets/target_test.go b/internal/tests/api/targets/target_test.go index d625a2c6d..a5d901228 100644 --- a/internal/tests/api/targets/target_test.go +++ b/internal/tests/api/targets/target_test.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/boundary/internal/credential/vault" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/servers/controller" - "github.com/hashicorp/boundary/internal/target" + "github.com/hashicorp/boundary/internal/target/tcp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -250,7 +250,7 @@ func TestSet_Errors(t *testing.T) { assert.NotNil(apiErr) assert.Nil(tar) - _, err = tarClient.Read(tc.Context(), target.TcpTargetPrefix+"_doesntexis") + _, err = tarClient.Read(tc.Context(), tcp.TargetPrefix+"_doesntexis") require.Error(err) apiErr = api.AsServerError(err) assert.NotNil(apiErr) diff --git a/internal/tests/cli/boundary/_helpers.bash b/internal/tests/cli/boundary/_helpers.bash index ec87630ae..3b65f5de1 100644 --- a/internal/tests/cli/boundary/_helpers.bash +++ b/internal/tests/cli/boundary/_helpers.bash @@ -22,3 +22,11 @@ function strip_all() { echo "$1" | tr -d '"' | tr -d '\'\' } +function has_status_code() { + local json=$1 + local code=$2 + if [ echo "$json"|jq -c ".status_code == $code" ]; then + return 1 + fi + +} \ No newline at end of file diff --git a/internal/tests/cli/boundary/groups.bats b/internal/tests/cli/boundary/groups.bats index 498b95da3..798dc1719 100644 --- a/internal/tests/cli/boundary/groups.bats +++ b/internal/tests/cli/boundary/groups.bats @@ -59,6 +59,7 @@ export NEW_GROUP='test' local gid=$(group_id $NEW_GROUP) run delete_group $gid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/host_catalogs.bats b/internal/tests/cli/boundary/host_catalogs.bats index 3e3630429..376b0afe4 100644 --- a/internal/tests/cli/boundary/host_catalogs.bats +++ b/internal/tests/cli/boundary/host_catalogs.bats @@ -44,6 +44,7 @@ export NEW_HOST_CATALOG='test' local hid=$(host_catalog_id $NEW_HOST_CATALOG $DEFAULT_P_ID) run delete_host_catalog $hid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/host_sets.bats b/internal/tests/cli/boundary/host_sets.bats index 4f1a17667..bd095bfce 100644 --- a/internal/tests/cli/boundary/host_sets.bats +++ b/internal/tests/cli/boundary/host_sets.bats @@ -58,6 +58,7 @@ export NEW_HOST_SET='test' local hsid=$(host_set_id $NEW_HOST_SET $DEFAULT_HOST_CATALOG) run delete_host_set $hsid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/hosts.bats b/internal/tests/cli/boundary/hosts.bats index f25e15880..91ae9ef3e 100644 --- a/internal/tests/cli/boundary/hosts.bats +++ b/internal/tests/cli/boundary/hosts.bats @@ -44,6 +44,7 @@ export NEW_HOST='test' local hid=$(host_id $NEW_HOST $DEFAULT_HOST_CATALOG) run delete_host $hid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/roles.bats b/internal/tests/cli/boundary/roles.bats index 6546f9eff..3555c0011 100644 --- a/internal/tests/cli/boundary/roles.bats +++ b/internal/tests/cli/boundary/roles.bats @@ -102,6 +102,7 @@ export NEW_GRANT='id=*;type=*;actions=create,read,update,delete,list' local rid=$(role_id $NEW_ROLE $DEFAULT_GLOBAL) run delete_role $rid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/scopes.bats b/internal/tests/cli/boundary/scopes.bats index af7ece028..6d27495a3 100644 --- a/internal/tests/cli/boundary/scopes.bats +++ b/internal/tests/cli/boundary/scopes.bats @@ -64,6 +64,7 @@ export NEW_PROJECT='test_project' local sid=$(scope_id $NEW_PROJECT $parent) run delete_scope $sid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } @@ -79,6 +80,7 @@ export NEW_PROJECT='test_project' local sid=$(scope_id $NEW_ORG $DEFAULT_GLOBAL) run delete_scope $sid echo "$output" + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/target.bats b/internal/tests/cli/boundary/target.bats index f53f2d4be..812ce0d9b 100644 --- a/internal/tests/cli/boundary/target.bats +++ b/internal/tests/cli/boundary/target.bats @@ -92,6 +92,7 @@ load _helpers @test "boundary/target: default user can delete target" { local id=$(target_id_from_name $DEFAULT_P_ID $TGT_NAME) run delete_target $id + run has_status_code "$output" "204" [ "$status" -eq 0 ] } diff --git a/internal/tests/cli/boundary/user.bats b/internal/tests/cli/boundary/user.bats index 9451ed655..8fac3eb86 100644 --- a/internal/tests/cli/boundary/user.bats +++ b/internal/tests/cli/boundary/user.bats @@ -68,7 +68,9 @@ export NEW_USER='test' @test "boundary/user: can delete $NEW_USER user" { login $DEFAULT_LOGIN local uid=$(user_id $NEW_USER) - run delete_user $uid + run delete_user $uid + echo $output + run has_status_code "$output" "204" [ "$status" -eq 0 ] } @@ -82,4 +84,4 @@ export NEW_USER='test' local aid=$(account_id $NEW_USER) run delete_account $aid [ "$status" -eq 0 ] -} \ No newline at end of file +} diff --git a/internal/types/subtypes/registry.go b/internal/types/subtypes/registry.go index 27423ad7a..10462cbd5 100644 --- a/internal/types/subtypes/registry.go +++ b/internal/types/subtypes/registry.go @@ -4,6 +4,7 @@ package subtypes import ( "fmt" "strings" + "sync" "github.com/hashicorp/boundary/internal/errors" ) @@ -22,13 +23,14 @@ func (t Subtype) String() string { // Registry stores a collection of boundary resource subtypes along with their // prefixes and allows for translating prefixes back to registered subtypes. -// Registry is not thread safe. type Registry struct { subtypesPrefixes map[string]Subtype knownSubtypes map[Subtype]interface{} + + sync.RWMutex } -// New Registry creates a new boundary resource subtype registry. +// NewRegistry creates a new boundary resource subtype registry. func NewRegistry() *Registry { return &Registry{ subtypesPrefixes: make(map[string]Subtype), @@ -39,6 +41,9 @@ func NewRegistry() *Registry { // SubtypeFromType returns the Subtype from the provided string or if // no Subtype was registered with that string Unknown is returned. func (r *Registry) SubtypeFromType(t string) Subtype { + r.RLock() + defer r.RUnlock() + st := Subtype(t) if _, ok := r.knownSubtypes[st]; !ok { return UnknownSubtype @@ -49,6 +54,9 @@ func (r *Registry) SubtypeFromType(t string) Subtype { // SubtypeFromId returns the Subtype from the provided id if the id's prefix // was registered with a Subtype. Otherwise Unknown is returned. func (r *Registry) SubtypeFromId(id string) Subtype { + r.RLock() + defer r.RUnlock() + i := strings.Index(id, "_") if i == -1 { return UnknownSubtype @@ -66,6 +74,9 @@ func (r *Registry) SubtypeFromId(id string) Subtype { // an error if the subtype has already been registered or if any of the // prefixes are associated with another subtype. func (r *Registry) Register(subtype Subtype, prefixes ...string) error { + r.Lock() + defer r.Unlock() + const op = "subtypes.(Registry).Register" if _, present := r.knownSubtypes[subtype]; present { return errors.NewDeprecated(errors.SubtypeAlreadyRegistered, op, fmt.Sprintf("subtype %q already registered", subtype)) diff --git a/scripts/trigger-merge-to-ent b/scripts/trigger-merge-to-ent new file mode 100755 index 000000000..1a6fb7a09 --- /dev/null +++ b/scripts/trigger-merge-to-ent @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if [[ -z "${CIRCLE_REPOSITORY_URL}" ]]; then + exit 0 +fi + +if [[ "${CIRCLE_REPOSITORY_URL}" == *"boundary-enterprise"* ]]; then + exit 0 +fi + +if [[ -z ${CIRCLE_CI_ENT_TOK} ]]; then + exit 0 +fi + +curl -u ${CIRCLE_CI_ENT_TOK}: \ + -d 'build_parameters[CIRCLE_JOB]=merge-oss-main' \ + https://circleci.com/api/v1.1/project/github/hashicorp/boundary-enterprise/tree/main diff --git a/sdk/pbs/controller/api/resources/targets/target.pb.go b/sdk/pbs/controller/api/resources/targets/target.pb.go index b099979fb..cf0c3a283 100644 --- a/sdk/pbs/controller/api/resources/targets/target.pb.go +++ b/sdk/pbs/controller/api/resources/targets/target.pb.go @@ -461,11 +461,11 @@ type Target struct { Version uint32 `protobuf:"varint,80,opt,name=version,proto3" json:"version,omitempty"` // The type of the Target. Type string `protobuf:"bytes,90,opt,name=type,proto3" json:"type,omitempty"` - // The IDs of the Host Sets associated with this Target. + // Output only. The IDs of the Host Sets associated with this Target. HostSetIds []string `protobuf:"bytes,100,rep,name=host_set_ids,proto3" json:"host_set_ids,omitempty"` // Output only. The Host Sets associated with this Target. HostSets []*HostSet `protobuf:"bytes,110,rep,name=host_sets,proto3" json:"host_sets,omitempty"` - // The IDs of the Host Sources associated with this Target. + // Output only. The IDs of the Host Sources associated with this Target. HostSourceIds []string `protobuf:"bytes,420,rep,name=host_source_ids,proto3" json:"host_source_ids,omitempty"` // Output only. The Host Sources associated with this Target. HostSources []*HostSource `protobuf:"bytes,430,rep,name=host_sources,proto3" json:"host_sources,omitempty"` @@ -475,7 +475,7 @@ type Target struct { SessionConnectionLimit *wrapperspb.Int32Value `protobuf:"bytes,130,opt,name=session_connection_limit,proto3" json:"session_connection_limit,omitempty"` // Optional boolean expression to filter the workers that are allowed to satisfy this request. WorkerFilter *wrapperspb.StringValue `protobuf:"bytes,140,opt,name=worker_filter,proto3" json:"worker_filter,omitempty"` - // The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. + // Output only. The IDs of the application credential library ids associated with this Target. Deprecated: use application_credential_source_ids instead. // // Deprecated: Do not use. ApplicationCredentialLibraryIds []string `protobuf:"bytes,150,rep,name=application_credential_library_ids,proto3" json:"application_credential_library_ids,omitempty"` @@ -483,7 +483,7 @@ type Target struct { // // Deprecated: Do not use. ApplicationCredentialLibraries []*CredentialLibrary `protobuf:"bytes,180,rep,name=application_credential_libraries,proto3" json:"application_credential_libraries,omitempty"` - // The IDs of the application credential source ids associated with this Target. + // Output only. The IDs of the application credential source ids associated with this Target. ApplicationCredentialSourceIds []string `protobuf:"bytes,400,rep,name=application_credential_source_ids,proto3" json:"application_credential_source_ids,omitempty"` // Output only. The application credential sources associated with this Target. ApplicationCredentialSources []*CredentialSource `protobuf:"bytes,410,rep,name=application_credential_sources,proto3" json:"application_credential_sources,omitempty"` diff --git a/testing/dbtest/docker/postgresql.conf b/testing/dbtest/docker/postgresql.conf index 2cfab2543..675082eee 100644 --- a/testing/dbtest/docker/postgresql.conf +++ b/testing/dbtest/docker/postgresql.conf @@ -1,9 +1,10 @@ listen_addresses = '*' # listen on all addresses so other containers can connect if need be. -max_connections = 200000 # set max connections to the limit to ensure tests can always get a connection. - # Note that this may need to be decreased if <4G are available for the container. -shared_buffers = 256MB # A more reasonable default compared to the 32MB default config. +max_connections = 1000 # set max connections a higher limit than default; this will be set to the max limit for CI runs + # the limit to ensure tests can always get a connection. +shared_buffers = 64MB # A more reasonable default compared to the 32MB default config. # This gives postgres more memory for storing results. But given that short lived nature of the # test databases and the relatively small data sets, there should not be a need to increase it much more then this. + # For CI runs this is increased to 256MB. dynamic_shared_memory_type = posix # Use POSIX shared memory allocated using shm_open fsync = off # Do not flush, can cause data corruption, but since it is for tests and the data is dropped anyway... synchronous_commit = off # Do not wait for WAL records to be written to disk before reporting success diff --git a/website/components/openapi-page/index.jsx b/website/components/openapi-page/index.jsx deleted file mode 100644 index a3853708b..000000000 --- a/website/components/openapi-page/index.jsx +++ /dev/null @@ -1,94 +0,0 @@ -import { useState, useRef } from 'react' -import Link from 'next/link' -import OperationObject from './partials/operation-object' -import HashiHead from '@hashicorp/react-head' -import DocsSidenav from '@hashicorp/react-docs-sidenav' -import Content from '@hashicorp/react-content' -import { getPathsFromSchema, getPropsForPage } from './utils/routing-utils' -import styles from './style.module.css' -import useOnClickOutside from 'lib/hooks/use-on-click-outside' - -function OpenApiPage({ - info, - operationCategory, - navData, - productName, - productSlug, - currentPath, -}) { - const operationsRef = useRef(null) - const [expandedOperations, setExpandedOperations] = useState([]) - useOnClickOutside(operationsRef, () => setExpandedOperations([])) - - function setOperationState(slug, isExpanded) { - const newStates = expandedOperations.filter((s) => s !== slug) - if (isExpanded) newStates.push(slug) - setExpandedOperations(newStates) - } - - const pageTitle = operationCategory ? operationCategory.name : info.title - - return ( -
- {info.title} -
-- Select a service from the sidebar. -
- > - ) - } - /> -{summary}
-Request
- {pathParams.length > 0 ? ( -Response
- {!!successResponse ? ( -- Successful Response -
-No response has been defined.
- )} -- {title} -
- {params.map((parameter, idx) => { - return ( -{name}{' '}
- {typeString}{' '}
- {data.required ? (
-
- Required
-
- ) : null}
- {data.title && (
- {data.title}
- )} - {data.description && ( -- {data.description} -
- )} - {hasProperties && ( -