diff --git a/Makefile b/Makefile index 3289f9b8eb..448e3a8ccc 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,8 @@ protobuild: @protoc-go-inject-tag -input=./internal/gen/controller/api/services/auth_method_service.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/authmethods/auth_method.pb.go @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/scopes/scope.pb.go - + @protoc-go-inject-tag -input=./internal/gen/controller/servers/services/session_service.pb.go + @protoc-go-inject-tag -input=./sdk/pbs/controller/api/resources/targets/target.pb.go # these protos, services and openapi artifacts are purely for testing purposes @protoc-go-inject-tag -input=./internal/gen/testing/event/event.pb.go diff --git a/go.mod b/go.mod index ba85d8c217..6248bd7fad 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/hashicorp/cap v0.1.1 github.com/hashicorp/dawdle v0.4.0 github.com/hashicorp/dbassert v0.0.0-20210708202608-ecf920cf1ed8 - github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 + github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 github.com/hashicorp/go-bexpr v0.1.10 github.com/hashicorp/go-cleanhttp v0.5.2 diff --git a/go.sum b/go.sum index c906495e02..fef78debfb 100644 --- a/go.sum +++ b/go.sum @@ -477,8 +477,9 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c h1:u1Zq+LfTXaagmJCHK3XH/fi7pk7oRY933VjGNG7/Ynw= +github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c/go.mod h1:NaXU8p/pl5a2RX/N0/yncinT3Iw5CLkbF4JRxAVnk3c= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= diff --git a/internal/db/read_writer.go b/internal/db/read_writer.go index 26a3ebf436..37c8592cbf 100644 --- a/internal/db/read_writer.go +++ b/internal/db/read_writer.go @@ -463,14 +463,15 @@ func (rw *Db) CreateItems(ctx context.Context, createItems []interface{}, opt .. // which almost always should be to rollback. Update returns the number of // rows updated. // -// Supported options: WithOplog, NewOplogMsg and WithVersion. +// Supported options: WithOplog, NewOplogMsg, WithWhere and WithVersion. // WithOplog will write an oplog entry for the update. NewOplogMsg // will return in-memory oplog message. WithOplog and NewOplogMsg cannot be // used together. If WithVersion is used, then the update will include the // version number in the update where clause, which basically makes the update // use optimistic locking and the update will only succeed if the existing rows // version matches the WithVersion option. Zero is not a valid value for the -// WithVersion option and will return an error. +// WithVersion option and will return an error. WithWhere allows specifying an +// additional constraint on the operation in addition to the PKs. func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string, setToNullPaths []string, opt ...Option) (int, error) { const op = "db.Update" if rw.underlying == nil { @@ -620,8 +621,8 @@ func (rw *Db) Update(ctx context.Context, i interface{}, fieldMaskPaths []string // Delete an object in the db with options: WithOplog, NewOplogMsg, WithWhere. // WithOplog will write an oplog entry for the delete. NewOplogMsg will return // in-memory oplog message. WithOplog and NewOplogMsg cannot be used together. -// WithWhere allows specifying a constraint. Delete returns the number of rows -// deleted and any errors. +//WithWhere allows specifying an additional constraint on the operation in +//addition to the PKs. Delete returns the number of rows deleted and any errors. func (rw *Db) Delete(ctx context.Context, i interface{}, opt ...Option) (int, error) { const op = "db.Delete" if rw.underlying == nil { diff --git a/internal/db/read_writer_ext_test.go b/internal/db/read_writer_ext_test.go index 889ecdaaa0..2405da0753 100644 --- a/internal/db/read_writer_ext_test.go +++ b/internal/db/read_writer_ext_test.go @@ -19,6 +19,7 @@ func TestDb_Create_OnConflict(t *testing.T) { wrapper := db.TestWrapper(t) conn, _ := db.TestSetup(t, "postgres") rw := db.New(conn) + db.TestCreateTables(t, conn) // create initial user for on conflict tests id, err := db.NewPublicId("test-user") diff --git a/internal/db/read_writer_test.go b/internal/db/read_writer_test.go index 86eac489a4..a45cfa993c 100644 --- a/internal/db/read_writer_test.go +++ b/internal/db/read_writer_test.go @@ -26,6 +26,7 @@ import ( func TestDb_UpdateUnsetField(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) rw := &Db{ underlying: db, } @@ -49,6 +50,7 @@ func TestDb_UpdateUnsetField(t *testing.T) { func TestDb_Update(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) now := ×tamp.Timestamp{Timestamp: timestamppb.Now()} publicId, err := NewPublicId("testuser") require.NoError(t, err) @@ -626,6 +628,7 @@ func (u *testUserWithVet) VetForWrite(ctx context.Context, r Reader, opType OpTy func TestDb_Create(t *testing.T) { // intentionally not run with t.Parallel so we don't need to use DoTx for the Create tests db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("simple", func(t *testing.T) { assert, require := assert.New(t), require.New(t) w := Db{underlying: db} @@ -801,6 +804,7 @@ func TestDb_Create(t *testing.T) { func TestDb_LookupByPublicId(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("simple", func(t *testing.T) { assert, require := assert.New(t), require.New(t) w := Db{underlying: db} @@ -856,6 +860,7 @@ func TestDb_LookupByPublicId(t *testing.T) { func TestDb_LookupWhere(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("simple", func(t *testing.T) { assert, require := assert.New(t), require.New(t) w := Db{underlying: db} @@ -907,6 +912,7 @@ func TestDb_LookupWhere(t *testing.T) { func TestDb_SearchWhere(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) knownUser := testUser(t, db, "zedUser", "", "") type args struct { @@ -1059,6 +1065,7 @@ func TestDb_Exec(t *testing.T) { t.Parallel() t.Run("update", func(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) require := require.New(t) w := Db{underlying: db} id := testId(t) @@ -1078,6 +1085,7 @@ func TestDb_DoTx(t *testing.T) { t.Parallel() ctx := context.TODO() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("valid-with-10-retries", func(t *testing.T) { assert, require := assert.New(t), require.New(t) w := &Db{underlying: db} @@ -1250,6 +1258,7 @@ func TestDb_DoTx(t *testing.T) { func TestDb_Delete(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) newUser := func() *db_test.TestUser { w := &Db{ underlying: db, @@ -1273,6 +1282,13 @@ func TestDb_Delete(t *testing.T) { "resource-public-id": []string{publicId}, } } + + // seed some test users, so we won't just happen to get a false positive + // with only 1 entry in the db + for i := 0; i < 1000; i++ { + _ = newUser() + } + type args struct { i *db_test.TestUser opt []Option @@ -1285,6 +1301,7 @@ func TestDb_Delete(t *testing.T) { args args want int wantOplog bool + wantFound bool wantErr bool wantErrIs errors.Code }{ @@ -1298,6 +1315,29 @@ func TestDb_Delete(t *testing.T) { want: 1, wantErr: false, }, + { + name: "with-where-no-delete", + underlying: db, + wrapper: TestWrapper(t), + args: args{ + i: newUser(), + opt: []Option{WithWhere("1 = ?", 2)}, + }, + wantFound: true, + want: 0, + wantErr: false, + }, + { + name: "with-where-and-delete", + underlying: db, + wrapper: TestWrapper(t), + args: args{ + i: newUser(), + opt: []Option{WithWhere("1 = ?", 1)}, + }, + want: 1, + wantErr: false, + }, { name: "valid-with-oplog", underlying: db, @@ -1385,6 +1425,11 @@ func TestDb_Delete(t *testing.T) { foundUser := tt.args.i.Clone().(*db_test.TestUser) foundUser.PublicId = tt.args.i.PublicId err = rw.LookupByPublicId(context.Background(), foundUser) + if tt.wantFound { + assert.NoError(err) + assert.Equal(tt.args.i.PublicId, foundUser.PublicId) + return + } assert.Error(err) assert.True(errors.Match(errors.T(errors.RecordNotFound), err)) @@ -1475,6 +1520,7 @@ func TestDb_Delete(t *testing.T) { func TestDb_ScanRows(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("valid", func(t *testing.T) { assert, require := assert.New(t), require.New(t) w := Db{underlying: db} @@ -1502,6 +1548,7 @@ func TestDb_ScanRows(t *testing.T) { func TestDb_Query(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) t.Run("valid", func(t *testing.T) { assert, require := assert.New(t), require.New(t) rw := Db{underlying: db} @@ -1530,6 +1577,7 @@ func TestDb_Query(t *testing.T) { func TestDb_CreateItems(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) testOplogResourceId := testId(t) createFn := func() []interface{} { @@ -1742,6 +1790,7 @@ func TestDb_CreateItems(t *testing.T) { func TestDb_DeleteItems(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) testOplogResourceId := testId(t) createFn := func() []interface{} { @@ -2031,6 +2080,7 @@ func testScooterAccessory(t *testing.T, conn *DB, scooterId, accessoryId uint32) func TestDb_LookupById(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) scooter := testScooter(t, db, "", 0) user := testUser(t, db, "", "", "") accessory := testAccessory(t, db, "test accessory") @@ -2170,6 +2220,7 @@ func TestDb_LookupById(t *testing.T) { func TestDb_GetTicket(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) type notReplayable struct{} tests := []struct { name string @@ -2229,6 +2280,7 @@ func TestDb_GetTicket(t *testing.T) { func TestDb_WriteOplogEntryWith(t *testing.T) { db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) w := Db{underlying: db} ticket, err := w.GetTicket(&db_test.TestUser{}) @@ -2959,6 +3011,7 @@ func TestDb_oplogMsgsForItems(t *testing.T) { func TestDb_lookupAfterWrite(t *testing.T) { t.Parallel() db, _ := TestSetup(t, "postgres") + TestCreateTables(t, db) scooter := testScooter(t, db, "", 0) user := testUser(t, db, "", "", "") type args struct { diff --git a/internal/db/schema/migrations/oss/postgres/19/03_db.up.sql b/internal/db/schema/migrations/oss/postgres/19/03_db.up.sql new file mode 100644 index 0000000000..79409adc31 --- /dev/null +++ b/internal/db/schema/migrations/oss/postgres/19/03_db.up.sql @@ -0,0 +1,23 @@ +begin; + +-- remove the internal/db test tables from the migrations. + +drop table if exists db_test_user cascade; +drop table if exists db_test_car cascade; +drop table if exists db_test_rental cascade; +drop table if exists db_test_scooter cascade; +drop table if exists db_test_accessory cascade; +drop table if exists db_test_scooter_accessory cascade; + + +delete from oplog_ticket where name in +( + 'db_test_user', + 'db_test_car', + 'db_test_rental', + 'db_test_scooter', + 'db_test_accessory', + 'db_test_scooter_accessory' +); + +commit; diff --git a/internal/db/testing.go b/internal/db/testing.go index 2fe1972551..6a5eaf8536 100644 --- a/internal/db/testing.go +++ b/internal/db/testing.go @@ -17,6 +17,7 @@ import ( wrapping "github.com/hashicorp/go-kms-wrapping" "github.com/hashicorp/go-kms-wrapping/wrappers/aead" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gorm.io/gorm/logger" ) @@ -227,3 +228,255 @@ func WithTemplate(template string) TestOption { o.withTemplate = template } } + +// TestCreateTables will create the test tables for the db pkg +func TestCreateTables(t *testing.T, conn *DB) { + t.Helper() + t.Cleanup(func() { testDropTables(t, conn) }) + require := require.New(t) + testCtx := context.Background() + rw := New(conn) + _, err := rw.Exec(testCtx, testQueryCreateTables, nil) + require.NoError(err) + _, err = rw.Exec(testCtx, testQueryAddOplogEntries, nil) + require.NoError(err) +} + +func testDropTables(t *testing.T, conn *DB) { + t.Helper() + require := require.New(t) + testCtx := context.Background() + rw := New(conn) + _, err := rw.Exec(testCtx, testQueryDropTables, nil) + require.NoError(err) + _, err = rw.Exec(testCtx, testQueryDeleteOplogEntries, nil) + require.NoError(err) +} + +const ( + testQueryCreateTables = ` +begin; + +-- create test tables used in the unit tests for the internal/db package +-- these tables (db_test_user, db_test_car, db_test_rental, db_test_scooter) are +-- not part of the boundary domain model... they are simply used for testing +-- the internal/db package +create table if not exists db_test_user ( + id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + public_id text not null unique, + name text unique, + phone_number text, + email text, + version wt_version +); + +create trigger + update_time_column +before +update on db_test_user + for each row execute procedure update_time_column(); + +-- define the immutable fields for db_test_user +create trigger + immutable_columns +before +update on db_test_user + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_user + for each row execute procedure default_create_time(); + +create trigger + update_version_column +after update on db_test_user + for each row execute procedure update_version_column(); + +create table if not exists db_test_car ( + id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + public_id text not null unique, + name text unique, + model text, + mpg smallint +); + +create trigger + update_time_column +before +update on db_test_car + for each row execute procedure update_time_column(); + +-- define the immutable fields for db_test_car +create trigger + immutable_columns +before +update on db_test_car + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_car + for each row execute procedure default_create_time(); + +create table if not exists db_test_rental ( + id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + public_id text not null unique, + name text unique, + user_id bigint not null references db_test_user(id), + car_id bigint not null references db_test_car(id) +); + +create trigger + update_time_column +before +update on db_test_rental + for each row execute procedure update_time_column(); + +-- define the immutable fields for db_test_rental +create trigger + immutable_columns +before +update on db_test_rental + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_rental + for each row execute procedure default_create_time(); + + +create table if not exists db_test_scooter ( + id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + private_id text not null unique, + name text unique, + model text, + mpg smallint +); + +create trigger + update_time_column +before +update on db_test_scooter + for each row execute procedure update_time_column(); + + +-- define the immutable fields for db_test_scooter +create trigger + immutable_columns +before +update on db_test_scooter + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_scooter + for each row execute procedure default_create_time(); + +create table if not exists db_test_accessory ( + accessory_id bigint generated always as identity primary key, + create_time wt_timestamp, + update_time wt_timestamp, + description text not null +); + +create trigger + update_time_column +before +update on db_test_accessory + for each row execute procedure update_time_column(); + +create trigger + immutable_columns +before +update on db_test_accessory + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_accessory + for each row execute procedure default_create_time(); + + +create table if not exists db_test_scooter_accessory ( + accessory_id bigint references db_test_accessory(accessory_id), + scooter_id bigint references db_test_scooter(id), + create_time wt_timestamp, + update_time wt_timestamp, + review text, + primary key(accessory_id, scooter_id) +); + +create trigger + update_time_column +before +update on db_test_scooter_accessory + for each row execute procedure update_time_column(); + +create trigger + immutable_columns +before +update on db_test_scooter_accessory + for each row execute procedure immutable_columns('create_time'); + +create trigger + default_create_time_column +before +insert on db_test_scooter_accessory + for each row execute procedure default_create_time(); + +commit; +` + testQueryDropTables = ` +begin; +drop table if exists db_test_user cascade; +drop table if exists db_test_car cascade; +drop table if exists db_test_rental cascade; +drop table if exists db_test_scooter cascade; +drop table if exists db_test_accessory cascade; +drop table if exists db_test_scooter_accessory cascade; +commit; +` + + testQueryAddOplogEntries = ` +begin; + +insert into oplog_ticket (name, version) +values + ('db_test_user', 1), + ('db_test_car', 1), + ('db_test_rental', 1), + ('db_test_scooter', 1), + ('db_test_accessory', 1), + ('db_test_scooter_accessory', 1); +commit; +` + testQueryDeleteOplogEntries = ` +begin; + +delete from oplog_ticket where name in +( + 'db_test_user', + 'db_test_car', + 'db_test_rental', + 'db_test_scooter', + 'db_test_accessory', + 'db_test_scooter_accessory' +); + +commit; +` +) diff --git a/internal/db/testing_test.go b/internal/db/testing_test.go index 75e0b4e89a..bffe0b3a97 100644 --- a/internal/db/testing_test.go +++ b/internal/db/testing_test.go @@ -23,6 +23,7 @@ func Test_Utils(t *testing.T) { func TestVerifyOplogEntry(t *testing.T) { db, _ := TestSetup(t, "postgres") assert := assert.New(t) + TestCreateTables(t, db) t.Run("valid", func(t *testing.T) { rw := Db{underlying: db} diff --git a/internal/gen/controller/servers/services/session_service.pb.go b/internal/gen/controller/servers/services/session_service.pb.go index 3927dd38e8..0159d05e09 100644 --- a/internal/gen/controller/servers/services/session_service.pb.go +++ b/internal/gen/controller/servers/services/session_service.pb.go @@ -28,10 +28,10 @@ type LookupSessionRequest struct { unknownFields protoimpl.UnknownFields // The session ID from the client - SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` // The name of the requesting worker, used for filtering to ensure this // worker is allowed to handle this session. - ServerId string `protobuf:"bytes,20,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty"` + ServerId string `protobuf:"bytes,20,opt,name=server_id,json=serverId,proto3" json:"server_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *LookupSessionRequest) Reset() { @@ -88,17 +88,17 @@ type LookupSessionResponse struct { unknownFields protoimpl.UnknownFields Authorization *targets.SessionAuthorizationData `protobuf:"bytes,10,opt,name=authorization,proto3" json:"authorization,omitempty"` - TofuToken string `protobuf:"bytes,20,opt,name=tofu_token,json=tofuToken,proto3" json:"tofu_token,omitempty"` - Version uint32 `protobuf:"varint,30,opt,name=version,proto3" json:"version,omitempty"` - Endpoint string `protobuf:"bytes,40,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - Expiration *timestamppb.Timestamp `protobuf:"bytes,50,opt,name=expiration,proto3" json:"expiration,omitempty"` - Status SESSIONSTATUS `protobuf:"varint,60,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty"` - ConnectionLimit int32 `protobuf:"varint,70,opt,name=connection_limit,json=connectionLimit,proto3" json:"connection_limit,omitempty"` - ConnectionsLeft int32 `protobuf:"varint,80,opt,name=connections_left,json=connectionsLeft,proto3" json:"connections_left,omitempty"` - HostId string `protobuf:"bytes,90,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` - HostSetId string `protobuf:"bytes,100,opt,name=host_set_id,json=hostSetId,proto3" json:"host_set_id,omitempty"` - TargetId string `protobuf:"bytes,110,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` - UserId string `protobuf:"bytes,120,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + TofuToken string `protobuf:"bytes,20,opt,name=tofu_token,json=tofuToken,proto3" json:"tofu_token,omitempty" class:"secret"` // @gotags: `class:"secret"` + Version uint32 `protobuf:"varint,30,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` + Endpoint string `protobuf:"bytes,40,opt,name=endpoint,proto3" json:"endpoint,omitempty" class:"public"` // @gotags: `class:"public"` + Expiration *timestamppb.Timestamp `protobuf:"bytes,50,opt,name=expiration,proto3" json:"expiration,omitempty" class:"public"` // @gotags: `class:"public"` + Status SESSIONSTATUS `protobuf:"varint,60,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` + ConnectionLimit int32 `protobuf:"varint,70,opt,name=connection_limit,json=connectionLimit,proto3" json:"connection_limit,omitempty" class:"public"` // @gotags: `class:"public"` + ConnectionsLeft int32 `protobuf:"varint,80,opt,name=connections_left,json=connectionsLeft,proto3" json:"connections_left,omitempty" class:"public"` // @gotags: `class:"public"` + HostId string `protobuf:"bytes,90,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" class:"public"` // @gotags: `class:"public"` + HostSetId string `protobuf:"bytes,100,opt,name=host_set_id,json=hostSetId,proto3" json:"host_set_id,omitempty" class:"public"` // @gotags: `class:"public"` + TargetId string `protobuf:"bytes,110,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty" class:"public"` // @gotags: `class:"public"` + UserId string `protobuf:"bytes,120,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *LookupSessionResponse) Reset() { @@ -222,11 +222,11 @@ type ActivateSessionRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - TofuToken string `protobuf:"bytes,20,opt,name=tofu_token,json=tofuToken,proto3" json:"tofu_token,omitempty"` - Version uint32 `protobuf:"varint,30,opt,name=version,proto3" json:"version,omitempty"` - WorkerId string `protobuf:"bytes,40,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` - Status SESSIONSTATUS `protobuf:"varint,50,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` + TofuToken string `protobuf:"bytes,20,opt,name=tofu_token,json=tofuToken,proto3" json:"tofu_token,omitempty" class:"secret"` // @gotags: `class:"secret"` + Version uint32 `protobuf:"varint,30,opt,name=version,proto3" json:"version,omitempty" class:"public"` // @gotags: `class:"public"` + WorkerId string `protobuf:"bytes,40,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" class:"public"` // @gotags: `class:"public"` + Status SESSIONSTATUS `protobuf:"varint,50,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *ActivateSessionRequest) Reset() { @@ -301,7 +301,7 @@ type ActivateSessionResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status SESSIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty"` + Status SESSIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *ActivateSessionResponse) Reset() { @@ -348,7 +348,7 @@ type CancelSessionRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CancelSessionRequest) Reset() { @@ -395,7 +395,7 @@ type CancelSessionResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status SESSIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty"` + Status SESSIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.SESSIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CancelSessionResponse) Reset() { @@ -442,8 +442,8 @@ type AuthorizeConnectionRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - WorkerId string `protobuf:"bytes,20,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` + WorkerId string `protobuf:"bytes,20,opt,name=worker_id,json=workerId,proto3" json:"worker_id,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *AuthorizeConnectionRequest) Reset() { @@ -497,9 +497,9 @@ type AuthorizeConnectionResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - Status CONNECTIONSTATUS `protobuf:"varint,20,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty"` - ConnectionsLeft int32 `protobuf:"varint,30,opt,name=connections_left,json=connectionsLeft,proto3" json:"connections_left,omitempty"` + ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" class:"public"` // @gotags: `class:"public"` + Status CONNECTIONSTATUS `protobuf:"varint,20,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` + ConnectionsLeft int32 `protobuf:"varint,30,opt,name=connections_left,json=connectionsLeft,proto3" json:"connections_left,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *AuthorizeConnectionResponse) Reset() { @@ -560,12 +560,12 @@ type ConnectConnectionRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - ClientTcpAddress string `protobuf:"bytes,20,opt,name=client_tcp_address,json=clientTcpAddress,proto3" json:"client_tcp_address,omitempty"` - ClientTcpPort uint32 `protobuf:"varint,30,opt,name=client_tcp_port,json=clientTcpPort,proto3" json:"client_tcp_port,omitempty"` - EndpointTcpAddress string `protobuf:"bytes,40,opt,name=endpoint_tcp_address,json=endpointTcpAddress,proto3" json:"endpoint_tcp_address,omitempty"` - EndpointTcpPort uint32 `protobuf:"varint,50,opt,name=endpoint_tcp_port,json=endpointTcpPort,proto3" json:"endpoint_tcp_port,omitempty"` - Type string `protobuf:"bytes,60,opt,name=type,proto3" json:"type,omitempty"` + ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" class:"public"` // @gotags: `class:"public"` + ClientTcpAddress string `protobuf:"bytes,20,opt,name=client_tcp_address,json=clientTcpAddress,proto3" json:"client_tcp_address,omitempty" class:"public"` // @gotags: `class:"public"` + ClientTcpPort uint32 `protobuf:"varint,30,opt,name=client_tcp_port,json=clientTcpPort,proto3" json:"client_tcp_port,omitempty" class:"public"` // @gotags: `class:"public"` + EndpointTcpAddress string `protobuf:"bytes,40,opt,name=endpoint_tcp_address,json=endpointTcpAddress,proto3" json:"endpoint_tcp_address,omitempty" class:"public"` // @gotags: `class:"public"` + EndpointTcpPort uint32 `protobuf:"varint,50,opt,name=endpoint_tcp_port,json=endpointTcpPort,proto3" json:"endpoint_tcp_port,omitempty" class:"public"` // @gotags: `class:"public"` + Type string `protobuf:"bytes,60,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *ConnectConnectionRequest) Reset() { @@ -647,7 +647,7 @@ type ConnectConnectionResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status CONNECTIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty"` + Status CONNECTIONSTATUS `protobuf:"varint,10,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *ConnectConnectionResponse) Reset() { @@ -694,10 +694,10 @@ type CloseConnectionRequestData struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - BytesUp uint64 `protobuf:"varint,20,opt,name=bytes_up,json=bytesUp,proto3" json:"bytes_up,omitempty"` - BytesDown uint64 `protobuf:"varint,30,opt,name=bytes_down,json=bytesDown,proto3" json:"bytes_down,omitempty"` - Reason string `protobuf:"bytes,40,opt,name=reason,proto3" json:"reason,omitempty"` + ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty" class:"public"` // @gotags: `class:"public"` + BytesUp uint64 `protobuf:"varint,20,opt,name=bytes_up,json=bytesUp,proto3" json:"bytes_up,omitempty" class:"public"` // @gotags: `class:"public"` + BytesDown uint64 `protobuf:"varint,30,opt,name=bytes_down,json=bytesDown,proto3" json:"bytes_down,omitempty" class:"public"` // @gotags: `class:"public"` + Reason string `protobuf:"bytes,40,opt,name=reason,proto3" json:"reason,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CloseConnectionRequestData) Reset() { @@ -765,7 +765,7 @@ type CloseConnectionRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CloseRequestData []*CloseConnectionRequestData `protobuf:"bytes,10,rep,name=close_request_data,json=closeRequestData,proto3" json:"close_request_data,omitempty"` + CloseRequestData []*CloseConnectionRequestData `protobuf:"bytes,10,rep,name=close_request_data,json=closeRequestData,proto3" json:"close_request_data,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CloseConnectionRequest) Reset() { @@ -813,7 +813,7 @@ type CloseConnectionResponseData struct { unknownFields protoimpl.UnknownFields ConnectionId string `protobuf:"bytes,10,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` - Status CONNECTIONSTATUS `protobuf:"varint,20,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty"` + Status CONNECTIONSTATUS `protobuf:"varint,20,opt,name=status,proto3,enum=controller.servers.services.v1.CONNECTIONSTATUS" json:"status,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CloseConnectionResponseData) Reset() { @@ -867,7 +867,7 @@ type CloseConnectionResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CloseResponseData []*CloseConnectionResponseData `protobuf:"bytes,10,rep,name=close_response_data,json=closeResponseData,proto3" json:"close_response_data,omitempty"` + CloseResponseData []*CloseConnectionResponseData `protobuf:"bytes,10,rep,name=close_response_data,json=closeResponseData,proto3" json:"close_response_data,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *CloseConnectionResponse) Reset() { diff --git a/internal/observability/event/cloudevents_formatter_node.go b/internal/observability/event/cloudevents_formatter_node.go index 6bcd56e51c..a96a1cbf85 100644 --- a/internal/observability/event/cloudevents_formatter_node.go +++ b/internal/observability/event/cloudevents_formatter_node.go @@ -37,9 +37,10 @@ func newCloudEventsFormatterFilter(source *url.URL, format cloudevents.Format, o opts := getOpts(opt...) n := cloudEventsFormatterFilter{ FormatterFilter: &cloudevents.FormatterFilter{ - Source: source, - Schema: opts.withSchema, - Format: format, + Source: source, + Schema: opts.withSchema, + Format: format, + SignEventTypes: []string{string(AuditType)}, }, } @@ -66,10 +67,28 @@ func newCloudEventsFormatterFilter(source *url.URL, format cloudevents.Format, o n.deny = append(n.deny, f) } } + defaultDenyFilters, err := defaultCloudEventsDenyFilters() + if err != nil { + return nil, err + } + n.deny = append(n.deny, defaultDenyFilters...) n.Predicate = newPredicate(n.allow, n.deny) return &n, nil } +func defaultCloudEventsDenyFilters() ([]*filter, error) { + const ( + op = "event.defaultCloudEventsDenyFilters" + // denyWorkStatusEvents is a default filter for worker to controller API status requests + denyWorkStatusEvents = `"/Data/RequestInfo/Method" contains "ServerCoordinationService/Status"` + ) + f, err := newFilter(denyWorkStatusEvents) + if err != nil { + return nil, fmt.Errorf("%s: unable to create deny filter for worker status events '%s': %w", op, denyWorkStatusEvents, err) + } + return []*filter{f}, nil +} + // Rotate supports rotating the filter's wrapper. No options are currently // supported. func (f *cloudEventsFormatterFilter) Rotate(w wrapping.Wrapper, _ ...Option) error { diff --git a/internal/observability/event/cloudevents_formatter_node_test.go b/internal/observability/event/cloudevents_formatter_node_test.go index 75dd9a7142..ba2fd42a20 100644 --- a/internal/observability/event/cloudevents_formatter_node_test.go +++ b/internal/observability/event/cloudevents_formatter_node_test.go @@ -130,10 +130,16 @@ func Test_newCloudEventsFormatterFilter(t *testing.T) { for _, f := range got.allow { assert.Contains(tt.wantAllow, f.raw) } - assert.Len(got.deny, len(tt.wantDeny)) + assert.Len(got.deny, len(tt.wantDeny)+1) // +1 since there's always a default deny + defs, err := defaultCloudEventsDenyFilters() + require.NoError(err) + for _, f := range defs { + tt.wantDeny = append(tt.wantDeny, f.raw) + } for _, f := range got.deny { assert.Contains(tt.wantDeny, f.raw) } + assert.Equal([]string{string(AuditType)}, got.SignEventTypes) }) } } diff --git a/internal/observability/event/hclog_formatter_node.go b/internal/observability/event/hclog_formatter_node.go index 8704f61284..1b51d844df 100644 --- a/internal/observability/event/hclog_formatter_node.go +++ b/internal/observability/event/hclog_formatter_node.go @@ -66,11 +66,29 @@ func newHclogFormatterFilter(jsonFormat bool, opt ...Option) (*hclogFormatterFil n.deny = append(n.deny, f) } } + defaultDenyFilters, err := defaultHclogEventsDenyFilters() + if err != nil { + return nil, err + } + n.deny = append(n.deny, defaultDenyFilters...) n.predicate = newPredicate(n.allow, n.deny) return &n, nil } +func defaultHclogEventsDenyFilters() ([]*filter, error) { + const ( + op = "event.defaultHclogEventsDenyFilters" + // denyWorkStatusEvents is a default filter for worker to controller API status requests + denyWorkStatusEvents = `"/RequestInfo/Method" contains "ServerCoordinationService/Status"` + ) + f, err := newFilter(denyWorkStatusEvents) + if err != nil { + return nil, fmt.Errorf("%s: unable to create deny filter for worker status events '%s': %w", op, denyWorkStatusEvents, err) + } + return []*filter{f}, nil +} + // Rotate supports rotating the filter's wrapper. No options are currently // supported. func (f *hclogFormatterFilter) Rotate(w wrapping.Wrapper, _ ...Option) error { diff --git a/internal/observability/event/hclog_formatter_node_test.go b/internal/observability/event/hclog_formatter_node_test.go index cf7ca1a4da..aea34f13e6 100644 --- a/internal/observability/event/hclog_formatter_node_test.go +++ b/internal/observability/event/hclog_formatter_node_test.go @@ -387,7 +387,12 @@ func Test_newHclogFormatterFilter(t *testing.T) { for _, f := range got.allow { assert.Contains(tt.wantAllow, f.raw) } - assert.Len(got.deny, len(tt.wantDeny)) + assert.Len(got.deny, len(tt.wantDeny)+1) // +1 since there's always a default deny + defs, err := defaultHclogEventsDenyFilters() + require.NoError(err) + for _, f := range defs { + tt.wantDeny = append(tt.wantDeny, f.raw) + } for _, f := range got.deny { assert.Contains(tt.wantDeny, f.raw) } diff --git a/internal/oplog/testing_test.go b/internal/oplog/testing_test.go index 93436e7c1b..3478813b6e 100644 --- a/internal/oplog/testing_test.go +++ b/internal/oplog/testing_test.go @@ -75,7 +75,7 @@ func Test_testInitStore(t *testing.T) { testInitStore(t, cleanup, url) const query = ` -select count(*) from information_schema."tables" t where table_name = 'db_test_user'; +select count(*) from information_schema."tables" t where table_name = 'boundary_schema_version'; ` db, err := common.SqlOpen("postgres", url) require.NoError(err) 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 d521735222..58d5a1cedf 100644 --- a/internal/proto/local/controller/api/resources/targets/v1/target.proto +++ b/internal/proto/local/controller/api/resources/targets/v1/target.proto @@ -11,230 +11,234 @@ import "controller/api/resources/scopes/v1/scope.proto"; import "controller/custom_options/v1/options.proto"; message HostSource { - // Output only. The ID of the Host Set. - string id = 10; + // Output only. The ID of the Host Set. + string id = 10; - // Output only. The Host Catalog to which this Host Source belongs. - string host_catalog_id = 20 [json_name="host_catalog_id"]; + // Output only. The Host Catalog to which this Host Source belongs. + string host_catalog_id = 20 [json_name = "host_catalog_id"]; } message HostSet { - // Output only. The ID of the Host Set. - string id = 10; + // Output only. The ID of the Host Set. + string id = 10; - // Output only. The Host Catalog to which this Host Set belongs. - string host_catalog_id = 20 [json_name="host_catalog_id"]; + // Output only. The Host Catalog to which this Host Set belongs. + string host_catalog_id = 20 [json_name = "host_catalog_id"]; } message CredentialSource { - // The ID of the Credential. May be empty if the credential is dynamically generated from a library. - string id = 10; + // The ID of the Credential. May be empty if the credential is dynamically generated from a library. + string id = 10; - // Output only. The name of the Credential source. - string name = 20; + // Output only. The name of the Credential source. + string name = 20; - // Output only. The description of the Credential source. - string description = 30; + // Output only. The description of the Credential source. + string description = 30; - // Output only. The Credential Store to which this Credential source belongs. - string credential_store_id = 40 [json_name="credential_store_id"]; + // Output only. The Credential Store to which this Credential source belongs. + string credential_store_id = 40 [json_name = "credential_store_id"]; - // Output only. The type of the credential source (e.g. "vault"; not the type of the credential itself). - string type = 60; + // Output only. The type of the credential source (e.g. "vault"; not the type of the credential itself). + string type = 60; } message CredentialLibrary { - // The ID of the Credential Library. - string id = 10; + // The ID of the Credential Library. + string id = 10; - // Output only. The name of the Credential Library. - string name = 20; + // Output only. The name of the Credential Library. + string name = 20; - // Output only. The description of the Credential Library. - string description = 30; + // Output only. The description of the Credential Library. + string description = 30; - // Output only. The Credential Store to which this Credential Library belongs. - string credential_store_id = 40 [json_name="credential_store_id"]; + // Output only. The Credential Store to which this Credential Library belongs. + string credential_store_id = 40 [json_name = "credential_store_id"]; - // Output only. The type of the credential library. - string type = 60; + // Output only. The type of the credential library. + string type = 60; } // The actual secret for a session credential. message SessionSecret { - // Output only. The base64-encoded value representing the raw bytes from the - // credential provider. - string raw = 10; + // Output only. The base64-encoded value representing the raw bytes from the + // credential provider. + string raw = 10; - // Output only. The decoded raw string, if a JSON object. - google.protobuf.Struct decoded = 20; + // Output only. The decoded raw string, if a JSON object. + google.protobuf.Struct decoded = 20; } // Credential information for a session. message SessionCredential { - // Output only. The credential source information. - CredentialSource credential_source = 1; + // Output only. The credential source information. + CredentialSource credential_source = 1; - // Output only. The library which generated this credential. Deprecated: use credential_source instead. - CredentialLibrary credential_library = 10 [deprecated = true]; + // Output only. The library which generated this credential. Deprecated: use credential_source instead. + CredentialLibrary credential_library = 10 [deprecated = true]; - // Output only. The secret of this credential base64 encoded. - SessionSecret secret = 20; + // Output only. The secret of this credential base64 encoded. + SessionSecret secret = 20; } // Target contains all fields related to a Target resource message Target { - // Output only. The ID of the resource. - string id = 10; + // Output only. The ID of the resource. + string id = 10; - // The Scope of of this resource. This must be defined for creation of this resource, but is otherwise output only. - string scope_id = 20 [json_name="scope_id"]; + // The Scope of of this resource. This must be defined for creation of this resource, but is otherwise output only. + string scope_id = 20 [json_name = "scope_id"]; - // Output only. Scope information for this resource. - resources.scopes.v1.ScopeInfo scope = 30; - - // Required name for identification purposes. - google.protobuf.StringValue name = 40 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"name" that: "name"}]; + // Output only. Scope information for this resource. + resources.scopes.v1.ScopeInfo scope = 30; - // Optional user-set description for identification purposes. - google.protobuf.StringValue description = 50 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this:"description" that: "description"}]; + // Required name for identification purposes. + google.protobuf.StringValue name = 40 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "name" that: "name" }]; - // Output only. The time this resource was created. - google.protobuf.Timestamp created_time = 60 [json_name="created_time"]; + // Optional user-set description for identification purposes. + google.protobuf.StringValue description = 50 [(custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "description" that: "description" }]; - // Output only. The time this resource was last updated. - google.protobuf.Timestamp updated_time = 70 [json_name="updated_time"]; + // Output only. The time this resource was created. + google.protobuf.Timestamp created_time = 60 [json_name = "created_time"]; - // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. - // The mutation will fail if the version does not match the latest known good version. - uint32 version = 80; + // Output only. The time this resource was last updated. + google.protobuf.Timestamp updated_time = 70 [json_name = "updated_time"]; - // The type of the Target. - string type = 90; + // Version is used in mutation requests, after the initial creation, to ensure this resource has not changed. + // The mutation will fail if the version does not match the latest known good version. + uint32 version = 80; - // Output only. The IDs of the Host Sets associated with this Target. - repeated string host_set_ids = 100 [json_name="host_set_ids"]; + // The type of the Target. + string type = 90; - // Output only. The Host Sets associated with this Target. - repeated HostSet host_sets = 110 [json_name="host_sets"]; + // 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 IDs of the Host Sources associated with this Target. - repeated string host_source_ids = 420 [json_name="host_source_ids"]; + // Output only. The Host Sets associated with this Target. + repeated HostSet host_sets = 110 [json_name = "host_sets"]; - // Output only. The Host Sources associated with this Target. - repeated HostSource host_sources = 430 [json_name="host_sources"]; + // Output only. The IDs of the Host Sources associated with this Target. + repeated string host_source_ids = 420 [json_name = "host_source_ids"]; - // Maximum total lifetime of a created Session, in seconds. - google.protobuf.UInt32Value session_max_seconds = 120 [json_name="session_max_seconds", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this: "session_max_seconds" that: "SessionMaxSeconds"}]; + // Output only. The Host Sources associated with this Target. + repeated HostSource host_sources = 430 [json_name = "host_sources"]; - // Maximum number of connections allowed in a Session. Unlimited is indicated by the value -1. - google.protobuf.Int32Value session_connection_limit = 130 [json_name="session_connection_limit", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this: "session_connection_limit" that: "SessionConnectionLimit"}]; + // Maximum total lifetime of a created Session, in seconds. + google.protobuf.UInt32Value session_max_seconds = 120 + [json_name = "session_max_seconds", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "session_max_seconds" that: "SessionMaxSeconds" }]; - // 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"}]; + // Maximum number of connections allowed in a Session. Unlimited is indicated by the value -1. + google.protobuf.Int32Value session_connection_limit = 130 + [json_name = "session_connection_limit", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "session_connection_limit" that: "SessionConnectionLimit" }]; - // 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]; + // 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" }]; - // 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"]; + // 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]; - // Output only. The IDs of the egress credential source ids associated with this Target. - repeated string egress_credential_source_ids = 500 [json_name="egress_credential_source_ids"]; - // Output only. The egress credential sources associated with this Target. - repeated CredentialSource egress_credential_sources = 510 [json_name="egress_credential_sources"]; + // 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"]; - // The attributes that are applicable for the specific Target. - google.protobuf.Struct attributes = 200 [(custom_options.v1.generate_sdk_option) = true]; + // Output only. The IDs of the egress credential source ids associated with this Target. + repeated string egress_credential_source_ids = 500 [json_name = "egress_credential_source_ids"]; + // Output only. The egress credential sources associated with this Target. + repeated CredentialSource egress_credential_sources = 510 [json_name = "egress_credential_sources"]; - // Output only. The available actions on this resource for this user. - repeated string authorized_actions = 300 [json_name="authorized_actions"]; + // The attributes that are applicable for the specific Target. + google.protobuf.Struct attributes = 200 [(custom_options.v1.generate_sdk_option) = true]; + + // Output only. The available actions on this resource for this user. + repeated string authorized_actions = 300 [json_name = "authorized_actions"]; } // TcpTargetAttributes contains attributes relevant to Targets of type "tcp" message TcpTargetAttributes { - // The default TCP port that will be used when connecting to the endpoint unless overridden by a Host Set or Host. - google.protobuf.UInt32Value default_port = 10 [json_name="default_port", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = {this: "attributes.default_port" that: "DefaultPort"}]; + // The default TCP port that will be used when connecting to the endpoint unless overridden by a Host Set or Host. + google.protobuf.UInt32Value default_port = 10 + [json_name = "default_port", (custom_options.v1.generate_sdk_option) = true, (custom_options.v1.mask_mapping) = { this: "attributes.default_port" that: "DefaultPort" }]; } // WorkerInfo contains information about workers, returned in to the client in SessionAuthorization message WorkerInfo { - // Output only. The address of the worker. - string address = 10; + // Output only. The address of the worker. + string address = 10; // @gotags: `class:"public"` } // SessionAuthorizationData contains the fields needed by the proxy command to connect to a worker. It is marshaled inside the SessionAuthorization message. message SessionAuthorizationData { - // Output only. The ID of the session. - string session_id = 10 [json_name="session_id"]; + // Output only. The ID of the session. + string session_id = 10 [json_name = "session_id"]; // @gotags: `class:"public"` - // Output only. The ID of the Target authorizing this session. - string target_id = 20 [json_name="target_id"]; + // Output only. The ID of the Target authorizing this session. + string target_id = 20 [json_name = "target_id"]; // @gotags: `class:"public"` - // Output only. Scope information for this the Target that authorized this session. - resources.scopes.v1.ScopeInfo scope = 30; + // Output only. Scope information for this the Target that authorized this session. + resources.scopes.v1.ScopeInfo scope = 30; - // Output only. The time this resource was created. - google.protobuf.Timestamp created_time = 40 [json_name="created_time"]; + // Output only. The time this resource was created. + google.protobuf.Timestamp created_time = 40 [json_name = "created_time"]; // @gotags: `class:"public"` - // Output only. Type of the session (e.g. tcp, ssh, etc.). - string type = 80; + // Output only. Type of the session (e.g. tcp, ssh, etc.). + string type = 80; // @gotags: `class:"public"` - // Output only. The connection limit being applied to this session. -1 means unlimited. This is not actually enforced on the client side but it provides for better listener handling by including it. - int32 connection_limit = 90 [json_name="connection_limit"]; + // Output only. The connection limit being applied to this session. -1 means unlimited. This is not actually enforced on the client side but it provides for better listener handling by including it. + int32 connection_limit = 90 [json_name = "connection_limit"]; - // Output only. The certificate to use when connecting. Raw DER bytes. - bytes certificate = 120; + // Output only. The certificate to use when connecting. Raw DER bytes. + bytes certificate = 120; // @gotags: `class:"secret"` - // Output only. The private key to use when connecting. We are using Ed25519, so this is purely raw bytes, no marshaling. - bytes private_key = 130 [json_name="private_key"]; + // Output only. The private key to use when connecting. We are using Ed25519, so this is purely raw bytes, no marshaling. + bytes private_key = 130 [json_name = "private_key"]; // @gotags: `class:"secret"` - // Output only. The host ID...not used for security purposes, but for some special command handling (e.g. ssh host key aliasing). - string host_id = 140; + // Output only. The host ID...not used for security purposes, but for some special command handling (e.g. ssh host key aliasing). + string host_id = 140; // @gotags: `class:"public"` - // Output only. The endpoint, for some special command handling. - string endpoint = 141; + // Output only. The endpoint, for some special command handling. + string endpoint = 141; // @gotags: `class:"public"` - // Output only. Worker information. The first worker in the array should be prioritized. - repeated WorkerInfo worker_info = 150 [json_name="worker_info"]; + // Output only. Worker information. The first worker in the array should be prioritized. + repeated WorkerInfo worker_info = 150 [json_name = "worker_info"]; } // SessionAuthorization contains all fields related to authorization for a Session. It's in the Targets package because it's returned by a Target's authorize action. message SessionAuthorization { - // Output only. The ID of the Session. - string session_id = 10 [json_name="session_id"]; + // Output only. The ID of the Session. + string session_id = 10 [json_name = "session_id"]; // @gotags: `class:"public"` - // Output only. The ID of the Target authorizing this Session. - string target_id = 20 [json_name="target_id"]; + // Output only. The ID of the Target authorizing this Session. + string target_id = 20 [json_name = "target_id"]; // @gotags: `class:"public"` - // Output only. Scope information for this resource. - resources.scopes.v1.ScopeInfo scope = 30; + // Output only. Scope information for this resource. + resources.scopes.v1.ScopeInfo scope = 30; - // Output only. The time this resource was created. - google.protobuf.Timestamp created_time = 40 [json_name="created_time"]; + // Output only. The time this resource was created. + google.protobuf.Timestamp created_time = 40 [json_name = "created_time"]; // @gotags: `class:"public"` - // Output only. The User for which this Session was authorized. - string user_id = 50 [json_name="user_id"]; + // Output only. The User for which this Session was authorized. + string user_id = 50 [json_name = "user_id"]; // @gotags: `class:"public"` - // Output only. The Host Set containing the Host being used for this Session. - string host_set_id = 60 [json_name="host_set_id"]; + // Output only. The Host Set containing the Host being used for this Session. + string host_set_id = 60 [json_name = "host_set_id"]; // @gotags: `class:"public"` - // Output only. The Host whose address is being used as the endpoint for this Session. - string host_id = 70 [json_name="host_id"]; + // Output only. The Host whose address is being used as the endpoint for this Session. + string host_id = 70 [json_name = "host_id"]; // @gotags: `class:"public"` - // Output only. Type of the Session (e.g. tcp, ssh, etc.). - string type = 80; + // Output only. Type of the Session (e.g. tcp, ssh, etc.). + string type = 80; // @gotags: `class:"public"` - // Output only. The marshaled SessionAuthorizationData message containing all information that the proxy needs. - string authorization_token = 90 [json_name="authorization_token"]; + // Output only. The marshaled SessionAuthorizationData message containing all information that the proxy needs. + string authorization_token = 90 [json_name = "authorization_token"]; // @gotags: `class:"private"` - // Output only. The endpoint address that the worker will connect to, useful for setting TLS parameters. - string endpoint = 100; + // Output only. The endpoint address that the worker will connect to, useful for setting TLS parameters. + string endpoint = 100; // @gotags: `class:"public"` - // Output only. The credentials for this session. - repeated SessionCredential credentials = 110 [json_name="credentials"]; + // Output only. The credentials for this session. + repeated SessionCredential credentials = 110 [json_name = "credentials"]; } diff --git a/internal/proto/local/controller/servers/services/v1/session_service.proto b/internal/proto/local/controller/servers/services/v1/session_service.proto index 01a5943443..8a297697f3 100644 --- a/internal/proto/local/controller/servers/services/v1/session_service.proto +++ b/internal/proto/local/controller/servers/services/v1/session_service.proto @@ -9,111 +9,111 @@ import "controller/api/resources/targets/v1/target.proto"; import "controller/servers/services/v1/server_coordination_service.proto"; service SessionService { - // GetSession allows a worker to retrieve session information from the - // controller. - rpc LookupSession(LookupSessionRequest) returns (LookupSessionResponse) {} - - // ActivateSession allows a worker to activate a session on a controller. - rpc ActivateSession(ActivateSessionRequest) returns (ActivateSessionResponse) {} + // GetSession allows a worker to retrieve session information from the + // controller. + rpc LookupSession(LookupSessionRequest) returns (LookupSessionResponse) {} - // CancelSession allows a worker to request that the controller cancel a session. - rpc CancelSession(CancelSessionRequest) returns (CancelSessionResponse) {} + // ActivateSession allows a worker to activate a session on a controller. + rpc ActivateSession(ActivateSessionRequest) returns (ActivateSessionResponse) {} - // AuthorizeConnection allows a worker to authorize a connection on a controller. - rpc AuthorizeConnection(AuthorizeConnectionRequest) returns (AuthorizeConnectionResponse) {} + // CancelSession allows a worker to request that the controller cancel a session. + rpc CancelSession(CancelSessionRequest) returns (CancelSessionResponse) {} - // ConnectConnection updates a connection to set it to connected - rpc ConnectConnection(ConnectConnectionRequest) returns (ConnectConnectionResponse) {} + // AuthorizeConnection allows a worker to authorize a connection on a controller. + rpc AuthorizeConnection(AuthorizeConnectionRequest) returns (AuthorizeConnectionResponse) {} - // CloseConnections updates a connection to set it to closed - rpc CloseConnection(CloseConnectionRequest) returns (CloseConnectionResponse) {} + // ConnectConnection updates a connection to set it to connected + rpc ConnectConnection(ConnectConnectionRequest) returns (ConnectConnectionResponse) {} + + // CloseConnections updates a connection to set it to closed + rpc CloseConnection(CloseConnectionRequest) returns (CloseConnectionResponse) {} } message LookupSessionRequest { - // The session ID from the client - string session_id = 10; - // The name of the requesting worker, used for filtering to ensure this - // worker is allowed to handle this session. - string server_id = 20; + // The session ID from the client + string session_id = 10; // @gotags: `class:"public"` + // The name of the requesting worker, used for filtering to ensure this + // worker is allowed to handle this session. + string server_id = 20; // @gotags: `class:"public"` } // LookupSessionResponse contains information necessary for a client to // establish a session. message LookupSessionResponse { - api.resources.targets.v1.SessionAuthorizationData authorization = 10; - string tofu_token = 20; - uint32 version = 30; - string endpoint = 40; - google.protobuf.Timestamp expiration = 50; - controller.servers.services.v1.SESSIONSTATUS status = 60; - int32 connection_limit = 70; - int32 connections_left = 80; - string host_id = 90; - string host_set_id = 100; - string target_id = 110; - string user_id = 120; + api.resources.targets.v1.SessionAuthorizationData authorization = 10; + string tofu_token = 20; // @gotags: `class:"secret"` + uint32 version = 30; // @gotags: `class:"public"` + string endpoint = 40; // @gotags: `class:"public"` + google.protobuf.Timestamp expiration = 50; // @gotags: `class:"public"` + controller.servers.services.v1.SESSIONSTATUS status = 60; // @gotags: `class:"public"` + int32 connection_limit = 70; // @gotags: `class:"public"` + int32 connections_left = 80; // @gotags: `class:"public"` + string host_id = 90; // @gotags: `class:"public"` + string host_set_id = 100; // @gotags: `class:"public"` + string target_id = 110; // @gotags: `class:"public"` + string user_id = 120; // @gotags: `class:"public"` } message ActivateSessionRequest { - string session_id = 10; - string tofu_token = 20; - uint32 version = 30; - string worker_id = 40; - controller.servers.services.v1.SESSIONSTATUS status = 50; + string session_id = 10; // @gotags: `class:"public"` + string tofu_token = 20; // @gotags: `class:"secret"` + uint32 version = 30; // @gotags: `class:"public"` + string worker_id = 40; // @gotags: `class:"public"` + controller.servers.services.v1.SESSIONSTATUS status = 50; // @gotags: `class:"public"` } message ActivateSessionResponse { - controller.servers.services.v1.SESSIONSTATUS status = 10; + controller.servers.services.v1.SESSIONSTATUS status = 10; // @gotags: `class:"public"` } message CancelSessionRequest { - string session_id = 10; + string session_id = 10; // @gotags: `class:"public"` } message CancelSessionResponse { - controller.servers.services.v1.SESSIONSTATUS status = 10; + controller.servers.services.v1.SESSIONSTATUS status = 10; // @gotags: `class:"public"` } message AuthorizeConnectionRequest { - string session_id = 10; - string worker_id = 20; + string session_id = 10; // @gotags: `class:"public"` + string worker_id = 20; // @gotags: `class:"public"` } message AuthorizeConnectionResponse { - string connection_id = 10; - controller.servers.services.v1.CONNECTIONSTATUS status = 20; - int32 connections_left = 30; + string connection_id = 10; // @gotags: `class:"public"` + controller.servers.services.v1.CONNECTIONSTATUS status = 20; // @gotags: `class:"public"` + int32 connections_left = 30; // @gotags: `class:"public"` } message ConnectConnectionRequest { - string connection_id = 10; - string client_tcp_address = 20; - uint32 client_tcp_port = 30; - string endpoint_tcp_address = 40; - uint32 endpoint_tcp_port = 50; - string type = 60; + string connection_id = 10; // @gotags: `class:"public"` + string client_tcp_address = 20; // @gotags: `class:"public"` + uint32 client_tcp_port = 30; // @gotags: `class:"public"` + string endpoint_tcp_address = 40; // @gotags: `class:"public"` + uint32 endpoint_tcp_port = 50; // @gotags: `class:"public"` + string type = 60; // @gotags: `class:"public"` } message ConnectConnectionResponse { - controller.servers.services.v1.CONNECTIONSTATUS status = 10; + controller.servers.services.v1.CONNECTIONSTATUS status = 10; // @gotags: `class:"public"` } message CloseConnectionRequestData { - string connection_id = 10; - uint64 bytes_up = 20; - uint64 bytes_down = 30; - string reason = 40; + string connection_id = 10; // @gotags: `class:"public"` + uint64 bytes_up = 20; // @gotags: `class:"public"` + uint64 bytes_down = 30; // @gotags: `class:"public"` + string reason = 40; // @gotags: `class:"public"` } message CloseConnectionRequest { - repeated CloseConnectionRequestData close_request_data = 10; + repeated CloseConnectionRequestData close_request_data = 10; // @gotags: `class:"public"` } message CloseConnectionResponseData { - string connection_id = 10; - controller.servers.services.v1.CONNECTIONSTATUS status = 20; + string connection_id = 10; + controller.servers.services.v1.CONNECTIONSTATUS status = 20; // @gotags: `class:"public"` } message CloseConnectionResponse { - repeated CloseConnectionResponseData close_response_data = 10; + repeated CloseConnectionResponseData close_response_data = 10; // @gotags: `class:"public"` } diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service.go b/internal/servers/controller/handlers/authmethods/authmethod_service.go index 4bf43902a9..9bf287c0cc 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/boundary/internal/servers/controller/handlers" "github.com/hashicorp/boundary/internal/servers/controller/handlers/accounts" "github.com/hashicorp/boundary/internal/servers/controller/handlers/authtokens" + "github.com/hashicorp/boundary/internal/servers/controller/handlers/managed_groups" "github.com/hashicorp/boundary/internal/types/action" "github.com/hashicorp/boundary/internal/types/resource" "github.com/hashicorp/boundary/internal/types/scope" @@ -60,7 +61,8 @@ var ( } collectionTypeMap = map[resource.Type]action.ActionSet{ - resource.Account: accounts.CollectionActions, + resource.Account: accounts.CollectionActions, + resource.ManagedGroup: managed_groups.CollectionActions, } ) diff --git a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go index 8dbd20d936..577ec6275d 100644 --- a/internal/servers/controller/handlers/authmethods/authmethod_service_test.go +++ b/internal/servers/controller/handlers/authmethods/authmethod_service_test.go @@ -64,6 +64,12 @@ var authorizedCollectionActions = map[string]*structpb.ListValue{ structpb.NewStringValue("list"), }, }, + "managed-groups": { + Values: []*structpb.Value{ + structpb.NewStringValue("create"), + structpb.NewStringValue("list"), + }, + }, } func TestGet(t *testing.T) { diff --git a/internal/servers/controller/interceptor.go b/internal/servers/controller/interceptor.go index c7887ce767..2f741f8b18 100644 --- a/internal/servers/controller/interceptor.go +++ b/internal/servers/controller/interceptor.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/observability/event" "github.com/hashicorp/boundary/internal/requests" + commonSrv "github.com/hashicorp/boundary/internal/servers/common" "github.com/hashicorp/boundary/internal/servers/controller/auth" "github.com/hashicorp/boundary/internal/servers/controller/common" "github.com/hashicorp/boundary/internal/servers/controller/handlers" @@ -268,3 +269,39 @@ func auditResponseInterceptor( return resp, err } } + +func workerRequestInfoInterceptor(ctx context.Context, eventer *event.Eventer) (grpc.UnaryServerInterceptor, error) { + const op = "worker.requestInfoInterceptor" + if eventer == nil { + return nil, errors.New(ctx, errors.InvalidParameter, op, "missing eventer") + + } + return func(interceptorCtx context.Context, + req interface{}, + srvInfo *grpc.UnaryServerInfo, + handler grpc.UnaryHandler) (interface{}, error) { + var err error + id, err := event.NewId(event.IdPrefix) + if err != nil { + event.WriteError(interceptorCtx, op, err, event.WithInfoMsg("unable to create id for event", "method", srvInfo.FullMethod)) + return nil, status.Errorf(codes.Internal, "Error creating id for event: %v", err) + } + info := &event.RequestInfo{ + EventId: id, + Id: commonSrv.GeneratedTraceId(interceptorCtx), + Method: srvInfo.FullMethod, + } + interceptorCtx, err = event.NewRequestInfoContext(interceptorCtx, info) + if err != nil { + event.WriteError(interceptorCtx, op, err, event.WithInfoMsg("unable to create context with request info", "method", srvInfo.FullMethod)) + return nil, status.Errorf(codes.Internal, "Error creating context with request info: %v", err) + } + interceptorCtx, err = event.NewEventerContext(interceptorCtx, eventer) + if err != nil { + event.WriteError(interceptorCtx, op, err, event.WithInfoMsg("unable to create context with eventer", "method", srvInfo.FullMethod)) + return nil, status.Errorf(codes.Internal, "Error creating context with eventer: %v", err) + } + // call the handler... + return handler(interceptorCtx, req) + }, nil +} diff --git a/internal/servers/controller/interceptor_test.go b/internal/servers/controller/interceptor_test.go index d1b0d57406..922e762d39 100644 --- a/internal/servers/controller/interceptor_test.go +++ b/internal/servers/controller/interceptor_test.go @@ -464,6 +464,97 @@ func Test_statusCodeInterceptor(t *testing.T) { } } +func Test_workerRequestInfoInterceptor(t *testing.T) { + factoryCtx := context.Background() + requestCtx := context.Background() + + returnCtxHandler := func(ctx context.Context, req interface{}) (interface{}, error) { + return ctx, nil + } + + c := event.TestEventerConfig(t, "Test_requestCtxInterceptor", event.TestWithAuditSink(t), event.TestWithObservationSink(t)) + testLock := &sync.Mutex{} + testLogger := hclog.New(&hclog.LoggerOptions{ + Mutex: testLock, + Name: "test", + }) + testEventer, err := event.NewEventer(testLogger, testLock, "Test_requestCtxInterceptor", c.EventerConfig) + require.NoError(t, err) + + tests := []struct { + name string + requestCtx context.Context + eventer *event.Eventer + wantFactoryErr bool + wantFactoryErrMatch *errors.Template + wantFactoryErrContains string + wantRequestErr bool + wantRequestErrMatch *errors.Template + wantRequestErrContains string + }{ + + { + name: "missing-eventer", + requestCtx: requestCtx, + wantFactoryErr: true, + wantFactoryErrMatch: errors.T(errors.InvalidParameter), + wantFactoryErrContains: "missing eventer", + }, + + { + name: "valid", + requestCtx: requestCtx, + eventer: testEventer, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert, require := assert.New(t), require.New(t) + interceptor, err := workerRequestInfoInterceptor(factoryCtx, tt.eventer) + 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) + requestInfo, found := event.RequestInfoFromContext(retCtx.(context.Context)) + require.True(found) + assert.NotNil(requestInfo) + assert.NotEmpty(requestInfo.Id) + assert.NotEmpty(requestInfo.EventId) + assert.Equal("FakeMethod", requestInfo.Method) + + eventer, found := event.EventerFromContext(retCtx.(context.Context)) + require.True(found) + assert.NotNil(eventer) + }) + } +} + type testGreeter struct { interceptor.UnimplementedGreeterServiceServer } diff --git a/internal/servers/controller/listeners.go b/internal/servers/controller/listeners.go index d5c175ff44..7c3452890c 100644 --- a/internal/servers/controller/listeners.go +++ b/internal/servers/controller/listeners.go @@ -12,6 +12,7 @@ import ( "sync" "time" + grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/cmd/base" pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services" @@ -123,9 +124,20 @@ func (c *Controller) startListeners(ctx context.Context) error { return fmt.Errorf("error getting sub-listener for worker proto: %w", err) } + workerReqInterceptor, err := workerRequestInfoInterceptor(ctx, c.conf.Eventer) + if err != nil { + return fmt.Errorf("error getting sub-listener for worker proto: %w", err) + } workerServer := grpc.NewServer( grpc.MaxRecvMsgSize(math.MaxInt32), grpc.MaxSendMsgSize(math.MaxInt32), + grpc.UnaryInterceptor( + grpc_middleware.ChainUnaryServer( + workerReqInterceptor, + auditRequestInterceptor(ctx), // before we get started, audit the request + auditResponseInterceptor(ctx), // as we finish, audit the response + ), + ), ) workerService := workers.NewWorkerServiceServer(c.ServersRepoFn, c.SessionRepoFn, c.workerStatusUpdateTimes, c.kms) pbs.RegisterServerCoordinationServiceServer(workerServer, workerService) diff --git a/sdk/go.mod b/sdk/go.mod index 18ac95c446..8fe2013f68 100644 --- a/sdk/go.mod +++ b/sdk/go.mod @@ -3,7 +3,7 @@ module github.com/hashicorp/boundary/sdk go 1.17 require ( - github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 + github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 github.com/hashicorp/go-hclog v0.16.2 github.com/hashicorp/go-kms-wrapping v0.6.6 diff --git a/sdk/go.sum b/sdk/go.sum index 6fbf66894e..39861da94d 100644 --- a/sdk/go.sum +++ b/sdk/go.sum @@ -242,8 +242,8 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/eventlogger v0.1.0/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= -github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144 h1:PCl0HtlVnIIloIozAKvjICu6K4IghKXAKNny3R3b2nI= -github.com/hashicorp/eventlogger v0.1.1-0.20211104100552-e1e801e50144/go.mod h1:a3IXf1aEJfpCPzseTOrwKj4fVW/Qn3oEmpQeaIznzH0= +github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c h1:u1Zq+LfTXaagmJCHK3XH/fi7pk7oRY933VjGNG7/Ynw= +github.com/hashicorp/eventlogger v0.1.1-0.20211106154408-4ff8da3a890c/go.mod h1:NaXU8p/pl5a2RX/N0/yncinT3Iw5CLkbF4JRxAVnk3c= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239 h1:Yh9tY0lige+y0trmjQeT9NRDo6+YvtNAzbmUNOsIUzI= github.com/hashicorp/eventlogger/filters/encrypt v0.1.6-0.20211027211326-5db60a48f239/go.mod h1:8rcez7Kw1zanB0/074qnOuGu7zxmNh9Xr2ZI+K4xVIA= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= diff --git a/sdk/pbs/controller/api/resources/targets/target.pb.go b/sdk/pbs/controller/api/resources/targets/target.pb.go index 3884329166..77df25ee14 100644 --- a/sdk/pbs/controller/api/resources/targets/target.pb.go +++ b/sdk/pbs/controller/api/resources/targets/target.pb.go @@ -755,7 +755,7 @@ type WorkerInfo struct { unknownFields protoimpl.UnknownFields // Output only. The address of the worker. - Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty"` + Address string `protobuf:"bytes,10,opt,name=address,proto3" json:"address,omitempty" class:"public"` // @gotags: `class:"public"` } func (x *WorkerInfo) Reset() { @@ -804,25 +804,25 @@ type SessionAuthorizationData struct { unknownFields protoimpl.UnknownFields // Output only. The ID of the session. - SessionId string `protobuf:"bytes,10,opt,name=session_id,proto3" json:"session_id,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The ID of the Target authorizing this session. - TargetId string `protobuf:"bytes,20,opt,name=target_id,proto3" json:"target_id,omitempty"` + TargetId string `protobuf:"bytes,20,opt,name=target_id,proto3" json:"target_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Scope information for this the Target that authorized this session. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` // Output only. The time this resource was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,40,opt,name=created_time,proto3" json:"created_time,omitempty"` + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,40,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Type of the session (e.g. tcp, ssh, etc.). - Type string `protobuf:"bytes,80,opt,name=type,proto3" json:"type,omitempty"` + Type string `protobuf:"bytes,80,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The connection limit being applied to this session. -1 means unlimited. This is not actually enforced on the client side but it provides for better listener handling by including it. ConnectionLimit int32 `protobuf:"varint,90,opt,name=connection_limit,proto3" json:"connection_limit,omitempty"` // Output only. The certificate to use when connecting. Raw DER bytes. - Certificate []byte `protobuf:"bytes,120,opt,name=certificate,proto3" json:"certificate,omitempty"` + Certificate []byte `protobuf:"bytes,120,opt,name=certificate,proto3" json:"certificate,omitempty" class:"secret"` // @gotags: `class:"secret"` // Output only. The private key to use when connecting. We are using Ed25519, so this is purely raw bytes, no marshaling. - PrivateKey []byte `protobuf:"bytes,130,opt,name=private_key,proto3" json:"private_key,omitempty"` + PrivateKey []byte `protobuf:"bytes,130,opt,name=private_key,proto3" json:"private_key,omitempty" class:"secret"` // @gotags: `class:"secret"` // Output only. The host ID...not used for security purposes, but for some special command handling (e.g. ssh host key aliasing). - HostId string `protobuf:"bytes,140,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` + HostId string `protobuf:"bytes,140,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The endpoint, for some special command handling. - Endpoint string `protobuf:"bytes,141,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + Endpoint string `protobuf:"bytes,141,opt,name=endpoint,proto3" json:"endpoint,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Worker information. The first worker in the array should be prioritized. WorkerInfo []*WorkerInfo `protobuf:"bytes,150,rep,name=worker_info,proto3" json:"worker_info,omitempty"` } @@ -943,25 +943,25 @@ type SessionAuthorization struct { unknownFields protoimpl.UnknownFields // Output only. The ID of the Session. - SessionId string `protobuf:"bytes,10,opt,name=session_id,proto3" json:"session_id,omitempty"` + SessionId string `protobuf:"bytes,10,opt,name=session_id,proto3" json:"session_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The ID of the Target authorizing this Session. - TargetId string `protobuf:"bytes,20,opt,name=target_id,proto3" json:"target_id,omitempty"` + TargetId string `protobuf:"bytes,20,opt,name=target_id,proto3" json:"target_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Scope information for this resource. Scope *scopes.ScopeInfo `protobuf:"bytes,30,opt,name=scope,proto3" json:"scope,omitempty"` // Output only. The time this resource was created. - CreatedTime *timestamppb.Timestamp `protobuf:"bytes,40,opt,name=created_time,proto3" json:"created_time,omitempty"` + CreatedTime *timestamppb.Timestamp `protobuf:"bytes,40,opt,name=created_time,proto3" json:"created_time,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The User for which this Session was authorized. - UserId string `protobuf:"bytes,50,opt,name=user_id,proto3" json:"user_id,omitempty"` + UserId string `protobuf:"bytes,50,opt,name=user_id,proto3" json:"user_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The Host Set containing the Host being used for this Session. - HostSetId string `protobuf:"bytes,60,opt,name=host_set_id,proto3" json:"host_set_id,omitempty"` + HostSetId string `protobuf:"bytes,60,opt,name=host_set_id,proto3" json:"host_set_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The Host whose address is being used as the endpoint for this Session. - HostId string `protobuf:"bytes,70,opt,name=host_id,proto3" json:"host_id,omitempty"` + HostId string `protobuf:"bytes,70,opt,name=host_id,proto3" json:"host_id,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. Type of the Session (e.g. tcp, ssh, etc.). - Type string `protobuf:"bytes,80,opt,name=type,proto3" json:"type,omitempty"` + Type string `protobuf:"bytes,80,opt,name=type,proto3" json:"type,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The marshaled SessionAuthorizationData message containing all information that the proxy needs. - AuthorizationToken string `protobuf:"bytes,90,opt,name=authorization_token,proto3" json:"authorization_token,omitempty"` + AuthorizationToken string `protobuf:"bytes,90,opt,name=authorization_token,proto3" json:"authorization_token,omitempty" class:"private"` // @gotags: `class:"private"` // Output only. The endpoint address that the worker will connect to, useful for setting TLS parameters. - Endpoint string `protobuf:"bytes,100,opt,name=endpoint,proto3" json:"endpoint,omitempty"` + Endpoint string `protobuf:"bytes,100,opt,name=endpoint,proto3" json:"endpoint,omitempty" class:"public"` // @gotags: `class:"public"` // Output only. The credentials for this session. Credentials []*SessionCredential `protobuf:"bytes,110,rep,name=credentials,proto3" json:"credentials,omitempty"` } diff --git a/website/content/docs/common-workflows/workflow-ssh-proxycommand.mdx b/website/content/docs/common-workflows/workflow-ssh-proxycommand.mdx index c9f9b1421c..5e91f85377 100644 --- a/website/content/docs/common-workflows/workflow-ssh-proxycommand.mdx +++ b/website/content/docs/common-workflows/workflow-ssh-proxycommand.mdx @@ -19,7 +19,7 @@ Start by configuring a `Host` entry in `.ssh/ssh_config` for `localhost`: ```bash Host ttcp_* - ProxyCommand sh -c "boundary connect -target-id %h -exec nc -- {{boundary.ip}} {{boundary.port}}" + ProxyCommand sh -c "boundary connect -target-id %n -exec nc -- {{boundary.ip}} {{boundary.port}}" ``` The `ProxyCommand` tells the SSH client to invoke `boundary connect`. We are passing the `-exec nc` flag to diff --git a/website/redirects.js b/website/redirects.js index a1326a120f..c89c4e8ee8 100644 --- a/website/redirects.js +++ b/website/redirects.js @@ -119,6 +119,11 @@ module.exports = [ destination: '/docs/concepts/domain-model/credential-libraries', permanent: false, }, + { + source: '/help/admin-ui/managed-groups', + destination: '/docs/concepts/domain-model/managed-groups', + permanent: false, + }, //////////////////////////////////////////// // Adding sub-resources to existing resource