// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package sessions_test import ( "context" "fmt" "testing" "time" "github.com/hashicorp/boundary/globals" "github.com/hashicorp/boundary/internal/auth" "github.com/hashicorp/boundary/internal/auth/ldap" "github.com/hashicorp/boundary/internal/authtoken" cred "github.com/hashicorp/boundary/internal/credential" credstatic "github.com/hashicorp/boundary/internal/credential/static" "github.com/hashicorp/boundary/internal/credential/vault" controllerauth "github.com/hashicorp/boundary/internal/daemon/controller/auth" "github.com/hashicorp/boundary/internal/daemon/controller/handlers" "github.com/hashicorp/boundary/internal/daemon/controller/handlers/sessions" "github.com/hashicorp/boundary/internal/db" "github.com/hashicorp/boundary/internal/db/timestamp" pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services" "github.com/hashicorp/boundary/internal/host/static" "github.com/hashicorp/boundary/internal/iam" "github.com/hashicorp/boundary/internal/kms" "github.com/hashicorp/boundary/internal/server" "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/v2" "github.com/hashicorp/go-uuid" "github.com/stretchr/testify/require" ) // testSession is a wrapper around "session" to help with setting up additional resources and mutating the session states // to fill in func testSession(t *testing.T, conn *db.DB, kmsCache *kms.Kms, wrapper wrapping.Wrapper, targetRepo *target.Repository, sessRepo *session.Repository, projectId string, isActive bool, ) *session.Session { cats := static.TestCatalogs(t, conn, projectId, 1) hosts := static.TestHosts(t, conn, cats[0].PublicId, 1) sets := static.TestSets(t, conn, cats[0].PublicId, 1) _ = static.TestSetMembers(t, conn, sets[0].PublicId, hosts) randomString, err := uuid.GenerateUUID() require.NoError(t, err) vaultStore := vault.TestCredentialStore(t, conn, wrapper, projectId, fmt.Sprintf("http://vault%s", randomString), fmt.Sprintf("vault-token-%s", randomString), fmt.Sprintf("accessor-%s", randomString)) libIds := vault.TestCredentialLibraries(t, conn, wrapper, vaultStore.GetPublicId(), 2) tcpTarget := tcp.TestTarget(context.Background(), t, conn, projectId, randomString, target.WithHostSources([]string{sets[0].GetPublicId()})) staticStore := credstatic.TestCredentialStore(t, conn, wrapper, projectId) upCreds := credstatic.TestUsernamePasswordCredentials(t, conn, wrapper, randomString, randomString, staticStore.GetPublicId(), projectId, 2) ids := target.CredentialSources{ BrokeredCredentialIds: []string{libIds[0].GetPublicId(), libIds[1].GetPublicId(), upCreds[0].GetPublicId(), upCreds[1].GetPublicId()}, } _, err = targetRepo.AddTargetCredentialSources(context.Background(), tcpTarget.GetPublicId(), tcpTarget.GetVersion(), ids) require.NoError(t, err) dynamicCreds := []*session.DynamicCredential{ session.NewDynamicCredential(libIds[0].GetPublicId(), cred.BrokeredPurpose), session.NewDynamicCredential(libIds[1].GetPublicId(), cred.BrokeredPurpose), } staticCreds := []*session.StaticCredential{ session.NewStaticCredential(upCreds[0].GetPublicId(), cred.BrokeredPurpose), session.NewStaticCredential(upCreds[1].GetPublicId(), cred.BrokeredPurpose), } at := authtoken.TestAuthToken(t, conn, kmsCache, globals.GlobalPrefix) uId := at.GetIamUserId() worker := server.TestPkiWorker(t, conn, wrapper, server.WithNewIdFunc(func(ctx context.Context) (string, error) { return server.NewWorkerIdFromScopeAndName(ctx, globals.GlobalPrefix, randomString) }), ) sess := session.TestSession(t, conn, wrapper, session.ComposedOf{ UserId: uId, HostId: hosts[0].GetPublicId(), TargetId: tcpTarget.GetPublicId(), HostSetId: sets[0].GetPublicId(), AuthTokenId: at.GetPublicId(), ProjectId: projectId, Endpoint: "tcp://127.0.0.1:22", ExpirationTime: timestamp.New(time.Now()), ConnectionLimit: 10, WorkerFilter: "worker", EgressWorkerFilter: "egress", IngressWorkerFilter: "ingress", DynamicCredentials: dynamicCreds, StaticCredentials: staticCreds, ProtocolWorkerId: worker.PublicId, CorrelationId: randomString, }) switch isActive { case true: session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22, "127.0.0.2", 23, "127.0.0.1") default: _, err := sessRepo.CancelSession(t.Context(), sess.PublicId, sess.Version) require.NoError(t, err) terminated, err := sessRepo.TerminateCompletedSessions(t.Context()) require.NoError(t, err) require.Equal(t, 1, terminated) } return sess } func TestGrants_ReadActions(t *testing.T) { ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrap := db.TestWrapper(t) iamRepo := iam.TestRepo(t, conn, wrap) rw := db.New(conn) kmsCache := kms.TestKms(t, conn, wrap) targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache) require.NoError(t, err) require.NoError(t, err) atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache) require.NoError(t, err) sessionRepoFn := func(option ...session.Option) (*session.Repository, error) { return session.NewRepository(ctx, rw, rw, kmsCache, option...) } iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } s, err := sessions.NewService(ctx, sessionRepoFn, iamRepoFn, 100) require.NoError(t, err) org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) sessionRepo, err := sessionRepoFn() require.NoError(t, err) proj1Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj1.PublicId, true) proj1Session2 := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj1.PublicId, true) proj2Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) proj3Session := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj3.PublicId, false) type result struct { wantErr error outputFields []string } t.Run("Get", func(t *testing.T) { testcases := []struct { name string userFunc func() (*iam.User, auth.Account) inputResultMap map[*pbs.GetSessionRequest]result }{ { name: "global role descendants grants can read all sessions", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=*;output_fields=id,target_id,scope,created_time,updated_time"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}}, {Id: proj2Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}}, {Id: proj3Session.PublicId}: {outputFields: []string{globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}}, }, }, { name: "org role individual project grants", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org1.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=expiration_time,auth_token_id,user_id,host_set_id,host_ids"}, GrantScopes: []string{proj1.PublicId}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {outputFields: []string{globals.ExpirationTimeField, globals.AuthTokenIdField, globals.UserIdField, globals.HostSetIdField, globals.HostIdsField}}, {Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()}, }, }, { name: "org role children grants", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org2.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=version,type,scope_id,endpoint,states,status,certificate,authorized_actions,connections,termination_reason"}, GrantScopes: []string{globals.GrantScopeChildren}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj2Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.ConnectionsField}}, {Id: proj3Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.TerminationReasonField}}, }, }, { name: "proj role this grants", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: proj1.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=version,type,scope_id,endpoint,states,status,certificate,termination_reason,authorized_actions,connections"}, GrantScopes: []string{globals.GrantScopeThis}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {outputFields: []string{globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.ConnectionsField}}, {Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()}, }, }, { name: "multiple project roles", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: proj1.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis}, }, { RoleScopeId: proj2.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=version"}, GrantScopes: []string{globals.GrantScopeThis}, }, { RoleScopeId: proj3.PublicId, Grants: []string{"id=*;type=session;actions=read;output_fields=id"}, GrantScopes: []string{globals.GrantScopeThis}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {outputFields: []string{globals.AuthorizedActionsField}}, {Id: proj2Session.PublicId}: {outputFields: []string{globals.VersionField}}, {Id: proj3Session.PublicId}: {outputFields: []string{globals.IdField}}, }, }, { name: "role with incorrect resource grant", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=worker;actions=*"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()}, }, }, { name: "role with incorrect action grant", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=create,update,delete"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }), inputResultMap: map[*pbs.GetSessionRequest]result{ {Id: proj1Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj2Session.PublicId}: {wantErr: handlers.ForbiddenError()}, {Id: proj3Session.PublicId}: {wantErr: handlers.ForbiddenError()}, }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { user, account := tc.userFunc() tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId()) require.NoError(t, err) fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo) for input, expect := range tc.inputResultMap { got, err := s.GetSession(fullGrantAuthCtx, input) if expect.wantErr != nil { require.ErrorIs(t, expect.wantErr, err) continue } require.NoError(t, err) handlers.TestAssertOutputFields(t, got.Item, expect.outputFields) } }) } }) t.Run("List", func(t *testing.T) { testcases := []struct { name string input *pbs.ListSessionsRequest userFunc func() (*iam.User, auth.Account) idOutputFieldsMap map[string][]string wantErr error }{ { name: "global list global role descendants grants can read all sessions", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=*;output_fields=id,target_id,scope,created_time,updated_time"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }), idOutputFieldsMap: map[string][]string{ proj1Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}, proj1Session2.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}, proj2Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}, proj3Session.PublicId: {globals.IdField, globals.TargetIdField, globals.ScopeField, globals.CreatedTimeField, globals.UpdatedTimeField}, }, }, { name: "global list org role individual project grants", userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org1.PublicId, Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,expiration_time,auth_token_id,user_id,host_set_id,host_ids"}, GrantScopes: []string{proj1.PublicId}, }, }), input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, idOutputFieldsMap: map[string][]string{ proj1Session.PublicId: {globals.IdField, globals.ExpirationTimeField, globals.AuthTokenIdField, globals.UserIdField, globals.HostSetIdField, globals.HostIdsField}, proj1Session2.PublicId: {globals.IdField, globals.ExpirationTimeField, globals.AuthTokenIdField, globals.UserIdField, globals.HostSetIdField, globals.HostIdsField}, }, }, { name: "global list org role children grants", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org2.PublicId, Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,version,type,scope_id,endpoint,states,status,certificate,authorized_actions,termination_reason"}, GrantScopes: []string{globals.GrantScopeChildren}, }, }), idOutputFieldsMap: map[string][]string{ proj2Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField}, proj3Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField, globals.TerminationReasonField}, }, }, { name: "proj role this grants", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: proj1.PublicId, Grants: []string{"id=*;type=session;actions=list,read;output_fields=id,version,type,scope_id,endpoint,states,status,certificate,termination_reason,authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis}, }, }), idOutputFieldsMap: map[string][]string{ proj1Session.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField}, proj1Session2.PublicId: {globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField}, }, }, { name: "multiple project roles", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: proj1.PublicId, Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis}, }, { RoleScopeId: proj2.PublicId, Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,version"}, GrantScopes: []string{globals.GrantScopeThis}, }, { RoleScopeId: proj3.PublicId, Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,state"}, GrantScopes: []string{globals.GrantScopeThis}, }, }), idOutputFieldsMap: map[string][]string{ proj1Session.PublicId: {globals.IdField, globals.AuthorizedActionsField}, proj1Session2.PublicId: {globals.IdField, globals.AuthorizedActionsField}, proj2Session.PublicId: {globals.IdField, globals.VersionField}, proj3Session.PublicId: {globals.IdField, globals.StateField}, }, }, { name: "iss 5003 resource id grant should not override all ids", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=*;actions=*;output_fields=id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants}, }, { RoleScopeId: globals.GlobalPrefix, Grants: []string{fmt.Sprintf("ids=%s;actions=*;output_fields=id,version", proj1Session2.PublicId)}, GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants}, }, }), idOutputFieldsMap: map[string][]string{ proj1Session.PublicId: {globals.IdField, globals.AuthorizedActionsField}, proj1Session2.PublicId: {globals.IdField, globals.AuthorizedActionsField}, proj2Session.PublicId: {globals.IdField, globals.AuthorizedActionsField}, proj3Session.PublicId: {globals.IdField, globals.AuthorizedActionsField}, }, }, { name: "role with incorrect resource grant", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=worker;actions=*"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }), wantErr: handlers.ForbiddenError(), }, { name: "role with list permission in global but not projects", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=*"}, GrantScopes: []string{globals.GrantScopeThis}, }, }), idOutputFieldsMap: map[string][]string{}, }, { name: "non-recursive list on global scope without grant", input: &pbs.ListSessionsRequest{ ScopeId: globals.GlobalPrefix, Recursive: true, IncludeTerminated: true, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=create,update,delete"}, GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants}, }, }), wantErr: handlers.ForbiddenError(), }, { name: "non-recursive list children grant", input: &pbs.ListSessionsRequest{ ScopeId: proj2.PublicId, }, userFunc: iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org2.PublicId, Grants: []string{"id=*;type=session;actions=list,no-op;output_fields=id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren}, }, }), idOutputFieldsMap: map[string][]string{ proj2Session.PublicId: {globals.IdField, globals.AuthorizedActionsField}, }, }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { user, account := tc.userFunc() tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId()) require.NoError(t, err) fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo) got, err := s.ListSessions(fullGrantAuthCtx, tc.input) if tc.wantErr != nil { require.ErrorIs(t, tc.wantErr, err) return } require.NoError(t, err) require.Len(t, got.Items, len(tc.idOutputFieldsMap)) for _, item := range got.Items { wantOutputFields, ok := tc.idOutputFieldsMap[item.GetId()] require.True(t, ok) handlers.TestAssertOutputFields(t, item, wantOutputFields) } }) } }) } func TestGrants_Cancel(t *testing.T) { ctx := context.Background() conn, _ := db.TestSetup(t, "postgres") wrap := db.TestWrapper(t) iamRepo := iam.TestRepo(t, conn, wrap) rw := db.New(conn) kmsCache := kms.TestKms(t, conn, wrap) targetRepo, err := target.NewRepository(ctx, rw, rw, kmsCache) require.NoError(t, err) require.NoError(t, err) atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache) require.NoError(t, err) sessionRepoFn := func(option ...session.Option) (*session.Repository, error) { return session.NewRepository(ctx, rw, rw, kmsCache, option...) } iamRepoFn := func() (*iam.Repository, error) { return iamRepo, nil } s, err := sessions.NewService(ctx, sessionRepoFn, iamRepoFn, 100) require.NoError(t, err) org1, proj1 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) org2, proj2 := iam.TestScopes(t, iamRepo, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) proj3 := iam.TestProject(t, iamRepo, org2.PublicId, iam.WithSkipDefaultRoleCreation(true), iam.WithSkipAdminRoleCreation(true)) sessionRepo, err := sessionRepoFn() require.NoError(t, err) testcases := []struct { name string setup func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) wantOutputField []string wantErr error }{ { name: "global role full grants can cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,target_id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeDescendants}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) return sess, userFn }, wantOutputField: []string{globals.IdField, globals.TargetIdField, globals.AuthorizedActionsField}, wantErr: nil, }, { name: "org role children grants can cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: org1.PublicId, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,version,type,scope_id,endpoint,states"}, GrantScopes: []string{globals.GrantScopeChildren}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj1.PublicId, true) return sess, userFn }, wantOutputField: []string{globals.IdField, globals.VersionField, globals.TypeField, globals.ScopeIdField, globals.EndpointField, globals.StatesField}, wantErr: nil, }, { name: "proj role children grants can cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: proj3.PublicId, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,status,certificate,authorized_actions"}, GrantScopes: []string{globals.GrantScopeThis}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj3.PublicId, true) return sess, userFn }, wantOutputField: []string{globals.IdField, globals.StatusField, globals.CertificateField, globals.AuthorizedActionsField}, wantErr: nil, }, { name: "global role specific scope grants can cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"}, GrantScopes: []string{proj2.PublicId}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) return sess, userFn }, wantOutputField: []string{globals.IdField, globals.AuthorizedActionsField}, wantErr: nil, }, { name: "global specific scope specific id grants can cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{fmt.Sprintf("id=%s;type=session;actions=cancel;output_fields=id,authorized_actions", sess.PublicId)}, GrantScopes: []string{proj2.PublicId}, }, }) return sess, userFn }, wantOutputField: []string{globals.IdField, globals.AuthorizedActionsField}, wantErr: nil, }, { name: "global role children scope grants cannot cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeChildren}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) return sess, userFn }, wantErr: handlers.ForbiddenError(), }, { name: "global role children scope grants cannot cancel", setup: func(t *testing.T) (*session.Session, func() (*iam.User, auth.Account)) { userFn := iam.TestUserManagedGroupGrantsFunc(t, conn, kmsCache, globals.GlobalPrefix, ldap.TestAuthMethodWithAccountInManagedGroup, []iam.TestRoleGrantsRequest{ { RoleScopeId: globals.GlobalPrefix, Grants: []string{"id=*;type=session;actions=cancel;output_fields=id,authorized_actions"}, GrantScopes: []string{globals.GrantScopeChildren}, }, }) sess := testSession(t, conn, kmsCache, wrap, targetRepo, sessionRepo, proj2.PublicId, true) return sess, userFn }, wantErr: handlers.ForbiddenError(), }, } for _, tc := range testcases { t.Run(tc.name, func(t *testing.T) { sess, userFn := tc.setup(t) user, account := userFn() mySession, _, err := sessionRepo.LookupSession(ctx, sess.PublicId) require.NoError(t, err) tok, err := atRepo.CreateAuthToken(ctx, user, account.GetPublicId()) require.NoError(t, err) fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo) got, err := s.CancelSession(fullGrantAuthCtx, &pbs.CancelSessionRequest{ Id: mySession.GetPublicId(), Version: mySession.Version, }) if tc.wantErr != nil { require.ErrorIs(t, err, tc.wantErr) return } require.NoError(t, err) handlers.TestAssertOutputFields(t, got.GetItem(), tc.wantOutputField) }) } }