Bug: fix groups in children scopes being filtered out by grants (#5418)

* add utility functions for grants tests

* use passed in scopeID to create user

* add test for groups list

* groups: set ParentScopeId before FetchActionSetForId

* add comment to TestRoleGrantsForToken

* lint and ran make gen

* fix import groups

* add an additional test case

* remove print

* changelog

* fix(alias): set parent scope id for alias resource (#5434)

set the `ParentScopeId` before fetching authorized actions for alias

* fix(worker): set parent scope id for worker resource (#5435)

set the `ParentScopeId` before fetching authorized actions for worker

* fix(user): children scopes being filtered out by grants for user (#5436)

* fix(user): children scopes being filtered out by grants for user

Resources are being filtered out due to missing ParentScopeId when constructing Resource to pass into authResults.FetchActionSetForId.

* fix(scope): set parent scope id for worker resource (#5439)

set the `ParentScopeId` before fetching authorized actions for worker

* fix(target): set parent scope id for target resource (#5447)

set the `ParentScopeId` before fetching authorized actions for target

* fix(roles): set parent scope id for roles resource (#5452)

* fix(roles): set parent scope id for roles resource

set the `ParentScopeId` before fetching authorized actions for role resource

* test(managed-group): add grants test coverage (#5453)

* fix(managed-group): set parent scope id for managed-group resource

set the ParentScopeId before fetching authorized actions for managed resource

* fix(host): set parent scope id for host resource (#5455)

set the `ParentScopeId` before fetching authorized actions for host resource

* fix(host-set): set parent scope id for host-set resource (#5456)

set the ParentScopeId before fetching authorized actions for host-set resource

* fix(host-catalog): set parent scope id for host-catalog resource (#5457)

set the ParentScopeId before fetching authorized actions for host-catalog resource

* fix(credential-store): set parent scope id for credential-store resource (#5458)

set the ParentScopeId before fetching authorized actions for credential-store resource

* fix(authmethods): set parent scope ID for auth methods resource (#5448)

* handlers/authmethods: fix children permission

* documentation

* formatting

* fix(credential): set parent scope id for credential resource (#5459)

set the `ParentScopeId` before fetching authorized actions for credential resource

* fix(accounts): bug grants filter children accounts (#5431)

* handlers/accounts: fix children grants filtering out results unexpectedly

* make gen

* fix(authtokens): set parent scope ID for auth token resource (#5451)

* fix authtokens not passing children scope ID

* make gen

* fix(credential-libraries): set parent scope ID (#5463)

* fix(common) set parent ID before fetching action setsparent ID before fetching action sets (#5467)

* fix(common) set parent ID before fetching action sets

* make gen

* additional test

* rename test

* more tests

* remove duplicate test

* make gen

* Update CHANGELOG.md

Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>

---------

Co-authored-by: Elim Tsiagbey <elim.tsiagbey@hashicorp.com>
Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
This commit is contained in:
authored and GitHub committed 2025-01-24 10:23:33 -08:00
1 parent 80c9f07a48
commit 7c4451dc73
39 files changed
+2676 -30

No files matched your search

+3
View File
@@ -46,6 +46,9 @@ maintainability of worker queries, and improve DB performance. ([PR](https://git
([PR](https://github.com/hashicorp/boundary/pull/5221)).
* Fix an issue where, when starting a session, the connection limit always displays 0.
([PR](https://github.com/hashicorp/boundary/pull/5396)).
* Fix bug which caused the `children` keyword not to apply the appropriate
permissions for a number of resources.
([PR](https://github.com/hashicorp/boundary/pull/5418)).
## 0.18.2 (2024/12/12)
### Bug fixes
+36
View File
@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/go-uuid"
"github.com/stretchr/testify/require"
)
@@ -46,3 +47,38 @@ func TestAuthToken(t testing.TB, conn *db.DB, kms *kms.Kms, scopeId string, opt
require.NoError(t, err)
return at
}
// TestRoleGrantsForToken contains information used by TestAuthTokenWithRoles to create
// roles and their associated grants (with grant scopes)
type TestRoleGrantsForToken struct {
RoleScopeID string
GrantStrings []string
GrantScopes []string
}
// TestAuthTokenWithRoles creates auth token associated with roles as requested by the caller along
// with any required resources to achieve said token
func TestAuthTokenWithRoles(t testing.TB, conn *db.DB, kms *kms.Kms, scopeId string, roles []TestRoleGrantsForToken) *AuthToken {
t.Helper()
ctx := context.Background()
rw := db.New(conn)
atRepo, err := NewRepository(ctx, rw, rw, kms)
require.NoError(t, err)
iamRepo, err := iam.NewRepository(ctx, rw, rw, kms)
require.NoError(t, err)
authMethod := password.TestAuthMethods(t, conn, scopeId, 1)[0]
loginName, err := uuid.GenerateUUID()
require.NoError(t, err)
acct := password.TestAccount(t, conn, authMethod.GetPublicId(), loginName)
user := iam.TestUser(t, iamRepo, scopeId, iam.WithAccountIds(acct.GetPublicId()))
for _, r := range roles {
role := iam.TestRoleWithGrants(t, conn, r.RoleScopeID, r.GrantScopes, r.GrantStrings)
_ = iam.TestUserRole(t, conn, role.PublicId, user.PublicId)
}
fullGrantToken, err := atRepo.CreateAuthToken(ctx, user, acct.GetPublicId())
require.NoError(t, err)
return fullGrantToken
}
@@ -5,11 +5,19 @@ package auth
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/common"
"github.com/hashicorp/boundary/internal/db"
authpb "github.com/hashicorp/boundary/internal/gen/controller/auth"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/requests"
"github.com/hashicorp/boundary/internal/server"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/stretchr/testify/require"
)
// DisabledAuthTestContext is meant for testing, and uses a context that has
@@ -30,3 +38,30 @@ func DisabledAuthTestContext(iamRepoFn common.IamRepoFactory, scopeId string, op
requestContext := context.WithValue(context.Background(), requests.ContextRequestInformationKey, &requests.RequestContext{})
return NewVerifierContext(requestContext, iamRepoFn, nil, nil, opts.withKms, &reqInfo)
}
// TestAuthContextFromToken creates an auth context with provided token
// This is used in conjunction with TestAuthTokenWithRoles which creates a test token
func TestAuthContextFromToken(t *testing.T, conn *db.DB, wrap wrapping.Wrapper, token *authtoken.AuthToken, iamRepo *iam.Repository) context.Context {
t.Helper()
ctx := context.Background()
rw := db.New(conn)
kmsCache := kms.TestKms(t, conn, wrap)
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
require.NoError(t, err)
serversRepoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kmsCache)
}
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
atRepoFn := func() (*authtoken.Repository, error) {
return atRepo, nil
}
fullGrantAuthCtx := NewVerifierContext(requests.NewRequestContext(ctx, requests.WithUserId(token.GetIamUserId())),
iamRepoFn, atRepoFn, serversRepoFn, kmsCache, &authpb.RequestInfo{
PublicId: token.PublicId,
Token: token.GetToken(),
TokenFormat: uint32(AuthTokenTypeBearer),
})
return fullGrantAuthCtx
}
@@ -150,6 +150,7 @@ func GetListingResourceInformation(
for _, scp := range scps {
scpId := scp.GetPublicId()
res.ScopeId = scpId
res.ParentScopeId = scp.GetParentId()
aSet := input.AuthResults.FetchActionSetForType(ctx,
// This is overridden by WithResource
resource.Unknown,
@@ -1488,9 +1488,10 @@ func validateSetPasswordRequest(ctx context.Context, req *pbs.SetPasswordRequest
func newOutputOpts(ctx context.Context, item auth.Account, authMethodId string, authResults requestauth.VerifyResults) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Account,
Pin: authMethodId,
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.ParentScopeId,
Type: resource.Account,
Pin: authMethodId,
}
res.Id = item.GetPublicId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions[globals.ResourceInfoFromPrefix(item.GetPublicId()).Subtype], requestauth.WithResource(&res)).Strings()
@@ -0,0 +1,111 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package accounts_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/accounts"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
func TestListPassword_Grants(t *testing.T) {
ctx := context.TODO()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrap)
iamRepo, err := iam.NewRepository(ctx, rw, rw, kms)
require.NoError(t, err)
pwRepoFn := func() (*password.Repository, error) {
return password.NewRepository(ctx, rw, rw, kms)
}
oidcRepoFn := func() (*oidc.Repository, error) {
return oidc.NewRepository(ctx, rw, rw, kms)
}
ldapRepoFn := func() (*ldap.Repository, error) {
return ldap.NewRepository(ctx, rw, rw, kms)
}
org, proj := iam.TestScopes(t, iam.TestRepo(t, conn, wrap))
orgAM := password.TestAuthMethod(t, conn, org.GetPublicId())
projAM := password.TestAuthMethod(t, conn, proj.GetPublicId())
orgPWAccounts := password.TestMultipleAccounts(t, conn, orgAM.GetPublicId(), 3)
projPWAccounts := password.TestMultipleAccounts(t, conn, projAM.GetPublicId(), 3)
testcases := []struct {
name string
roleRequest []authtoken.TestRoleGrantsForToken
input *pbs.ListAccountsRequest
wantAccountIDs []string
wantErr error
}{
{
name: "children grants global scope list org accounts return org accounts",
input: &pbs.ListAccountsRequest{
AuthMethodId: orgAM.PublicId,
PageSize: 100,
},
roleRequest: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=*;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantAccountIDs: []string{orgPWAccounts[0].PublicId, orgPWAccounts[1].PublicId, orgPWAccounts[2].PublicId},
wantErr: nil,
},
{
name: "children grants org scope list project accounts return project accounts",
input: &pbs.ListAccountsRequest{
AuthMethodId: projAM.PublicId,
PageSize: 100,
},
roleRequest: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.GetPublicId(),
GrantStrings: []string{"ids=*;type=*;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantAccountIDs: []string{projPWAccounts[0].PublicId, projPWAccounts[1].PublicId, projPWAccounts[2].PublicId},
wantErr: nil,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
s, err := accounts.NewService(ctx, pwRepoFn, oidcRepoFn, ldapRepoFn, 1000)
require.NoError(t, err, "Couldn't create new user service.")
tok := authtoken.TestAuthTokenWithRoles(t, conn, kms, globals.GlobalPrefix, tc.roleRequest)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, gErr := s.ListAccounts(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.Error(t, err)
require.ErrorIs(t, err, tc.wantErr)
return
}
require.NoError(t, gErr)
require.Len(t, got.Items, len(orgPWAccounts))
var gotIDs []string
for _, item := range got.Items {
gotIDs = append(gotIDs, item.Id)
}
require.ElementsMatch(t, tc.wantAccountIDs, gotIDs)
})
}
}
@@ -648,6 +648,7 @@ func newOutputOpts(ctx context.Context, item *target.Alias, scopeInfoMap map[str
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
if len(authorizedActions) == 0 {
return nil, false
@@ -0,0 +1,107 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package aliases_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/alias/target"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/aliases"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// Scopes [resource]:
// - globalAlias1 [globalAlias]
// - globalAlias2 [globalAlias]
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*target.Repository, error) {
return target.NewRepository(ctx, rw, rw, kmsCache)
}
s, err := aliases.NewService(ctx, repoFn, iamRepoFn, 1000)
require.NoError(t, err)
globalAlias1 := target.TestAlias(t, rw, "test.alias.one", target.WithDescription("alias_1"), target.WithName("alias_one"))
globalAlias2 := target.TestAlias(t, rw, "test.alias.two", target.WithDescription("alias_2"), target.WithName("alias_two"))
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListAliasesRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created aliases",
input: &pbs.ListAliasesRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=alias;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{globalAlias1.PublicId, globalAlias2.PublicId},
},
{
name: "global role grant this with a non-applicable type throws an error",
input: &pbs.ListAliasesRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: handlers.ApiErrorWithCode(codes.PermissionDenied),
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListAliases(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -1577,6 +1577,7 @@ func newOutputOpts(ctx context.Context, item auth.AuthMethod, scopeInfoMap map[s
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions[globals.ResourceInfoFromPrefix(item.GetPublicId()).Subtype], requestauth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
return nil, false, nil
@@ -0,0 +1,266 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package authmethods_test
import (
"context"
"slices"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth"
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/authtoken"
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/authmethods"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
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)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
oidcRepoFn := func() (*oidc.Repository, error) {
return oidc.NewRepository(ctx, rw, rw, kmsCache)
}
ldapRepoFn := func() (*ldap.Repository, error) {
return ldap.NewRepository(ctx, rw, rw, kmsCache)
}
pwRepoFn := func() (*password.Repository, error) {
return password.NewRepository(ctx, rw, rw, kmsCache)
}
atRepoFn := func() (*authtoken.Repository, error) {
return authtoken.NewRepository(ctx, rw, rw, kmsCache)
}
authMethodRepoFn := func() (*auth.AuthMethodRepository, error) {
return auth.NewAuthMethodRepository(ctx, rw, rw, kmsCache)
}
s, err := authmethods.NewService(ctx,
kmsCache,
pwRepoFn,
oidcRepoFn,
iamRepoFn,
atRepoFn,
ldapRepoFn,
authMethodRepoFn,
1000)
require.NoError(t, err)
org1, _ := iam.TestScopes(t, iamRepo)
org2, _ := iam.TestScopes(t, iamRepo)
databaseWrapper, err := kmsCache.GetWrapper(context.Background(), globals.GlobalPrefix, kms.KeyPurposeDatabase)
require.NoError(t, err)
oidcGlobal := oidc.TestAuthMethod(t, conn, databaseWrapper, globals.GlobalPrefix, oidc.InactiveState, "client-id", "secret")
oidcOrg1 := oidc.TestAuthMethod(t, conn, databaseWrapper, org1.GetPublicId(), oidc.InactiveState, "client-id", "secret")
oidcOrg2 := oidc.TestAuthMethod(t, conn, databaseWrapper, org2.GetPublicId(), oidc.InactiveState, "client-id", "secret")
ldapGlobal := ldap.TestAuthMethod(t, conn, wrap, globals.GlobalPrefix, []string{"ldaps://alice.com"})
ldapOrg1 := ldap.TestAuthMethod(t, conn, wrap, org1.GetPublicId(), []string{"ldaps://alice.com"})
ldapOrg2 := ldap.TestAuthMethod(t, conn, wrap, org2.GetPublicId(), []string{"ldaps://alice.com"})
pwGlobal := password.TestAuthMethod(t, conn, globals.GlobalPrefix)
pwOrg1 := password.TestAuthMethod(t, conn, org1.GetPublicId())
pwOrg2 := password.TestAuthMethod(t, conn, org2.GetPublicId())
// ignoreList consists of auth method IDs to omit from the result set
// this is to handle auth methods that are created during the auth token
// creation
ignoreList := []string{}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListAuthMethodsRequest
includeGlobalAuthMethods bool
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this and children returns all auth methods",
input: &pbs.ListAuthMethodsRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=auth-method;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
oidcGlobal.PublicId,
oidcOrg1.PublicId,
oidcOrg2.PublicId,
ldapGlobal.PublicId,
ldapOrg1.PublicId,
ldapOrg2.PublicId,
pwGlobal.PublicId,
pwOrg1.PublicId,
pwOrg2.PublicId,
},
},
{
name: "no grants return children org",
input: &pbs.ListAuthMethodsRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{},
wantErr: nil,
// auth methods in `global` are filtered out because the user does not have grants to read
// them in the global scope. Auth methods in org 1 and org 2 show up - my guess is because
// the u_anon grants allow auth methods to be read on any org.
wantIDs: []string{
// oidcGlobal.PublicId,
oidcOrg1.PublicId,
oidcOrg2.PublicId,
// ldapGlobal.PublicId,
ldapOrg1.PublicId,
ldapOrg2.PublicId,
// pwGlobal.PublicId,
pwOrg1.PublicId,
pwOrg2.PublicId,
},
},
{
name: "org role grant this and children returns auth methods in org1",
input: &pbs.ListAuthMethodsRequest{
ScopeId: org1.PublicId,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.PublicId,
GrantStrings: []string{"ids=*;type=auth-method;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{oidcOrg1.PublicId, ldapOrg1.PublicId, pwOrg1.PublicId},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
// auth method created during token generation will not be taken into considerations during this test
// adding to the ignoreList so it can be ignored later
ignoreList = append(ignoreList, tok.GetAuthMethodId())
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListAuthMethods(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
// authtoken.TestAuthTokenWithRoles creates an auth method at `global` scope so
// include AuthMethodID of the user used to generate AuthToken in the "wantIDs" set
// when listing auth methods at global scope
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
// do not include IDs in the ignore list in the
// result sets
if slices.Contains(ignoreList, g.Id) {
continue
}
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
t.Run("Get", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListAuthMethodsRequest
amIDExpectErrMap map[string]error
rolesToCreate []authtoken.TestRoleGrantsForToken
}{
{
name: "global role grant this and children returns all auth methods",
amIDExpectErrMap: map[string]error{
pwGlobal.GetPublicId(): nil,
pwOrg1.GetPublicId(): nil,
pwOrg2.GetPublicId(): nil,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=auth-method;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
},
{
name: "org role grant this and children returns auth methods in org1",
input: &pbs.ListAuthMethodsRequest{
ScopeId: org1.PublicId,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=auth-method;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
amIDExpectErrMap: map[string]error{
pwGlobal.GetPublicId(): handlers.ForbiddenError(),
pwOrg1.GetPublicId(): nil,
pwOrg2.GetPublicId(): nil,
},
},
{
name: "no grants return all auth methods",
input: &pbs.ListAuthMethodsRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
PageSize: 500,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{},
amIDExpectErrMap: map[string]error{
pwGlobal.GetPublicId(): handlers.ForbiddenError(),
pwOrg1.GetPublicId(): handlers.ForbiddenError(),
pwOrg2.GetPublicId(): handlers.ForbiddenError(),
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := controllerauth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
for amId, wantErr := range tc.amIDExpectErrMap {
_, err := s.GetAuthMethod(fullGrantAuthCtx, &pbs.GetAuthMethodRequest{
Id: amId,
})
if wantErr != nil {
require.ErrorIs(t, err, wantErr)
continue
}
require.NoError(t, err)
}
})
}
})
}
@@ -457,6 +457,8 @@ func newOutputOpts(ctx context.Context, item *authtoken.AuthToken, scopeInfoMap
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
if len(authorizedActions) == 0 {
return nil, false
@@ -0,0 +1,130 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package authtokens_test
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
at "github.com/hashicorp/boundary/internal/daemon/controller/handlers/authtokens"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - project level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project
// Scopes [resource]:
// - global [globalGroup]
// - org1 [org1Group]
// - proj1 [proj1Group]
// - org2 [org2Group]
// - proj2 [proj2Group]
// - proj3 [proj3Group]
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
kmsCache := kms.TestKms(t, conn, wrap)
atRepo, err := authtoken.NewRepository(ctx, rw, rw, kmsCache)
require.NoError(t, err)
atRepoFn := func() (*authtoken.Repository, error) { return atRepo, nil }
s, err := at.NewService(ctx, atRepoFn, iamRepoFn, 1000)
require.NoError(t, err)
org1, _ := iam.TestScopes(t, iamRepo)
org2, _ := iam.TestScopes(t, iamRepo)
globalAT := authtoken.TestAuthToken(t, conn, kmsCache, globals.GlobalPrefix)
org1AT := authtoken.TestAuthToken(t, conn, kmsCache, org1.GetPublicId())
org2AT := authtoken.TestAuthToken(t, conn, kmsCache, org2.GetPublicId())
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListAuthTokensRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this and children returns global and org groups",
input: &pbs.ListAuthTokensRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=auth-token;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{globalAT.PublicId, org1AT.PublicId, org2AT.PublicId},
},
{
name: "org role grant this and children returns org groups",
input: &pbs.ListAuthTokensRequest{
ScopeId: org1.PublicId,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.PublicId,
GrantStrings: []string{"ids=*;type=auth-token;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{org1AT.PublicId},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListAuthTokens(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
fmt.Println("ID:", g.GetId(), "Scope:", g.GetScopeId())
gotIDs = append(gotIDs, g.GetId())
}
for _, id := range tc.wantIDs {
require.Contains(t, gotIDs, id)
}
})
}
})
}
@@ -678,10 +678,11 @@ func newOutputOpts(
authResults auth.VerifyResults,
) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.CredentialLibrary,
Pin: credentialStoreId,
Id: item.GetPublicId(),
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.GetParentScopeId(),
Type: resource.CredentialLibrary,
Pin: credentialStoreId,
Id: item.GetPublicId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
@@ -0,0 +1,104 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package credentiallibraries_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentiallibraries"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/stretchr/testify/require"
)
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
kmsCache := kms.TestKms(t, conn, wrap)
sche := scheduler.TestScheduler(t, conn, wrap)
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kmsCache, sche)
}
s, err := credentiallibraries.NewService(ctx, iamRepoFn, vaultRepoFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
proj1CredStore := vault.TestCredentialStores(t, conn, wrap, proj.PublicId, 1)
proj1Libs := vault.TestCredentialLibraries(t, conn, wrap, proj1CredStore[0].GetPublicId(), 3)
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListCredentialLibrariesRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant descendant returns all credentials library",
input: &pbs.ListCredentialLibrariesRequest{
CredentialStoreId: proj1CredStore[0].GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=credential-library;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: []string{proj1Libs[0].GetPublicId(), proj1Libs[1].GetPublicId(), proj1Libs[2].GetPublicId()},
},
{
name: "org role grant this and children returns all credentials library",
input: &pbs.ListCredentialLibrariesRequest{
CredentialStoreId: proj1CredStore[0].GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.GetPublicId(),
GrantStrings: []string{"ids=*;type=credential-library;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{proj1Libs[0].GetPublicId(), proj1Libs[1].GetPublicId(), proj1Libs[2].GetPublicId()},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListCredentialLibraries(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -640,9 +640,10 @@ func newOutputOpts(
authResults auth.VerifyResults,
) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Credential,
Pin: credentialStoreId,
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.ParentScopeId,
Type: resource.Credential,
Pin: credentialStoreId,
}
res.Id = item.GetPublicId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
@@ -0,0 +1,139 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package credentials_test
import (
"context"
"fmt"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentials"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - proj level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project: this
// Scopes [resource]:
// - global
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kmsCache)
}
s, err := credentials.NewService(ctx, iamRepoFn, staticRepoFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
credStore := static.TestCredentialStore(t, conn, wrap, proj.GetPublicId())
var wantCreds []string
for i := 0; i < 10; i++ {
user := fmt.Sprintf("user-%d", i)
pass := fmt.Sprintf("pass-%d", i)
c := static.TestUsernamePasswordCredential(t, conn, wrap, user, pass, credStore.GetPublicId(), proj.GetPublicId())
require.NoError(t, err)
wantCreds = append(wantCreds, c.GetPublicId())
}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListCredentialsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created credentials",
input: &pbs.ListCredentialsRequest{
CredentialStoreId: credStore.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: wantCreds,
},
{
name: "org role grant this returns all created credentials",
input: &pbs.ListCredentialsRequest{
CredentialStoreId: credStore.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: wantCreds,
},
{
name: "project role grant this returns all created credentials",
input: &pbs.ListCredentialsRequest{
CredentialStoreId: credStore.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj.PublicId,
GrantStrings: []string{"ids=*;type=credential;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: wantCreds,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListCredentials(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -676,9 +676,10 @@ func newOutputOpts(
authzScopes map[string]*scopes.ScopeInfo,
) ([]handlers.Option, bool, error) {
res := perms.Resource{
Type: resource.CredentialStore,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
Type: resource.CredentialStore,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
ParentScopeId: authzScopes[item.GetProjectId()].GetParentScopeId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
@@ -0,0 +1,149 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package credentialstores_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/credential"
"github.com/hashicorp/boundary/internal/credential/static"
"github.com/hashicorp/boundary/internal/credential/vault"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/credentialstores"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/scheduler"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - proj level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project: this
// Scopes [resource]:
// - global
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
sche := scheduler.TestScheduler(t, conn, wrap)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
vaultRepoFn := func() (*vault.Repository, error) {
return vault.NewRepository(ctx, rw, rw, kmsCache, sche)
}
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kmsCache)
}
credStoreRepoFn := func() (*credential.StoreRepository, error) {
return credential.NewStoreRepository(context.Background(), rw, rw)
}
s, err := credentialstores.NewService(ctx, iamRepoFn, vaultRepoFn, staticRepoFn, credStoreRepoFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
var wantStores []string
for _, s := range vault.TestCredentialStores(t, conn, wrap, proj.GetPublicId(), 5) {
wantStores = append(wantStores, s.GetPublicId())
}
for _, s := range static.TestCredentialStores(t, conn, wrap, proj.GetPublicId(), 5) {
wantStores = append(wantStores, s.GetPublicId())
}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListCredentialStoresRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created credential stores",
input: &pbs.ListCredentialStoresRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=credential-store;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: wantStores,
},
{
name: "org role grant this returns all created credential stores",
input: &pbs.ListCredentialStoresRequest{
ScopeId: org.GetPublicId(),
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=credential-store;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: wantStores,
},
{
name: "project role grant this returns all created credential stores",
input: &pbs.ListCredentialStoresRequest{
ScopeId: proj.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj.PublicId,
GrantStrings: []string{"ids=*;type=credential-store;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: wantStores,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListCredentialStores(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -0,0 +1,119 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package groups_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/groups"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - project level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project
// Scopes [resource]:
// - global [globalGroup]
// - org1 [org1Group]
// - proj1 [proj1Group]
// - org2 [org2Group]
// - proj2 [proj2Group]
// - proj3 [proj3Group]
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
kmsCache := kms.TestKms(t, conn, wrap)
s, err := groups.NewService(ctx, repoFn, 1000)
require.NoError(t, err)
org1, _ := iam.TestScopes(t, iamRepo)
org2, proj2 := iam.TestScopes(t, iamRepo)
proj3 := iam.TestProject(t, iamRepo, org2.PublicId)
globalGroup := iam.TestGroup(t, conn, globals.GlobalPrefix, iam.WithDescription("global"), iam.WithName("global"))
org1Group := iam.TestGroup(t, conn, org1.GetPublicId(), iam.WithDescription("org1"), iam.WithName("org1"))
org2Group := iam.TestGroup(t, conn, org2.GetPublicId(), iam.WithDescription("org2"), iam.WithName("org2"))
proj2Group := iam.TestGroup(t, conn, proj2.GetPublicId(), iam.WithDescription("proj2"), iam.WithName("proj2"))
proj3Group := iam.TestGroup(t, conn, proj3.GetPublicId(), iam.WithDescription("proj3"), iam.WithName("proj3"))
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListGroupsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this and children returns global and org groups",
input: &pbs.ListGroupsRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{globalGroup.PublicId, org1Group.PublicId, org2Group.PublicId},
},
{
name: "org role grant this and children returns org and project groups",
input: &pbs.ListGroupsRequest{
ScopeId: org2.PublicId,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org2.PublicId,
GrantStrings: []string{"ids=*;type=group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{org2Group.PublicId, proj2Group.PublicId, proj3Group.PublicId},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListGroups(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -840,6 +840,7 @@ func newOutputOpts(ctx context.Context, item *iam.Group, scopeInfoMap map[string
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
if len(authorizedActions) == 0 {
return nil, false
@@ -0,0 +1,151 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package host_catalogs_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_catalogs"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/host"
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/plugin"
"github.com/hashicorp/boundary/internal/scheduler"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - proj level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project: this
// Scopes [resource]:
// - global
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
sche := scheduler.TestScheduler(t, conn, wrap)
staticRepoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kmsCache)
}
pluginHostRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kmsCache, sche, map[string]plgpb.HostPluginServiceClient{})
}
pluginRepoFn := func() (*plugin.Repository, error) {
return plugin.NewRepository(ctx, rw, rw, kmsCache)
}
iamRepoFn := func() (*iam.Repository, error) {
return iam.TestRepo(t, conn, wrap), nil
}
catalogServiceFn := func() (*host.CatalogRepository, error) {
return host.NewCatalogRepository(ctx, rw, rw)
}
s, err := host_catalogs.NewService(ctx, staticRepoFn, pluginHostRepoFn, pluginRepoFn, iamRepoFn, catalogServiceFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 5)
var wantHcs []string
for _, h := range hcs {
wantHcs = append(wantHcs, h.GetPublicId())
}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListHostCatalogsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created host catalogs",
input: &pbs.ListHostCatalogsRequest{
ScopeId: proj.GetPublicId(),
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=host-catalog;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: wantHcs,
},
{
name: "org role grant this returns all created host catalogs",
input: &pbs.ListHostCatalogsRequest{
ScopeId: proj.GetPublicId(),
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=host-catalog;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: wantHcs,
},
{
name: "project role grant this returns all created host catalogs",
input: &pbs.ListHostCatalogsRequest{
ScopeId: proj.GetPublicId(),
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj.PublicId,
GrantStrings: []string{"ids=*;type=host-catalog;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: wantHcs,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListHostCatalogs(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -775,9 +775,10 @@ func newOutputOpts(
pluginMap map[string]*plugin.Plugin,
) ([]handlers.Option, bool, error) {
res := perms.Resource{
Type: resource.HostCatalog,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
Type: resource.HostCatalog,
Id: item.GetPublicId(),
ScopeId: item.GetProjectId(),
ParentScopeId: scopeInfoMap[item.GetProjectId()].GetParentScopeId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
@@ -0,0 +1,140 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package host_sets_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/host_sets"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/scheduler"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - proj level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project: this
// Scopes [resource]:
// - global
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
sche := scheduler.TestScheduler(t, conn, wrap)
pluginRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kmsCache, sche, map[string]plgpb.HostPluginServiceClient{})
}
repoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kmsCache)
}
s, err := host_sets.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)
hc := hcs[0]
hsets := static.TestSets(t, conn, hc.GetPublicId(), 5)
var wantHostSets []string
for _, h := range hsets {
wantHostSets = append(wantHostSets, h.GetPublicId())
}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListHostSetsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created host-set",
input: &pbs.ListHostSetsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=host-set;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: wantHostSets,
},
{
name: "org role grant this returns all created host-set",
input: &pbs.ListHostSetsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=host-set;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: wantHostSets,
},
{
name: "project role grant this returns all created host-set",
input: &pbs.ListHostSetsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj.PublicId,
GrantStrings: []string{"ids=*;type=host-set;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: wantHostSets,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListHostSets(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -755,10 +755,11 @@ func (s Service) deleteFromRepo(ctx context.Context, projectId, id string) (bool
func newOutputOpts(ctx context.Context, item host.Set, plg *plugins.PluginInfo, authResults auth.VerifyResults) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Id: item.GetPublicId(),
Type: resource.HostSet,
Pin: item.GetCatalogId(),
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.ParentScopeId,
Id: item.GetPublicId(),
Type: resource.HostSet,
Pin: item.GetCatalogId(),
}
res.Id = item.GetPublicId()
idActions := idActionsTypeMap[globals.ResourceInfoFromPrefix(item.GetPublicId()).Subtype]
@@ -0,0 +1,142 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package hosts_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/hosts"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
hostplugin "github.com/hashicorp/boundary/internal/host/plugin"
"github.com/hashicorp/boundary/internal/host/static"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/scheduler"
plgpb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - proj level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project: this
// Scopes [resource]:
// - global
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
sche := scheduler.TestScheduler(t, conn, wrap)
pluginRepoFn := func() (*hostplugin.Repository, error) {
return hostplugin.NewRepository(ctx, rw, rw, kmsCache, sche, map[string]plgpb.HostPluginServiceClient{})
}
repoFn := func() (*static.Repository, error) {
return static.NewRepository(ctx, rw, rw, kmsCache)
}
s, err := hosts.NewService(ctx, repoFn, pluginRepoFn, 1000)
require.NoError(t, err)
org, proj := iam.TestScopes(t, iamRepo)
hcs := static.TestCatalogs(t, conn, proj.GetPublicId(), 1)
hc := hcs[0]
hset := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
var wantHs []string
testHosts := static.TestHosts(t, conn, hc.GetPublicId(), 10)
static.TestSetMembers(t, conn, hset.GetPublicId(), testHosts)
for _, h := range testHosts {
wantHs = append(wantHs, h.GetPublicId())
}
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListHostsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created hosts",
input: &pbs.ListHostsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: wantHs,
},
{
name: "org role grant this returns all created hosts",
input: &pbs.ListHostsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: wantHs,
},
{
name: "project role grant this returns all created hosts",
input: &pbs.ListHostsRequest{
HostCatalogId: hc.GetPublicId(),
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj.PublicId,
GrantStrings: []string{"ids=*;type=host;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: wantHs,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListHosts(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -622,10 +622,11 @@ func toPluginInfo(plg *plugin.Plugin) *plugins.PluginInfo {
func newOutputOpts(ctx context.Context, item host.Host, plg *plugin.Plugin, authResults auth.VerifyResults) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.Host,
Pin: item.GetCatalogId(),
Id: item.GetPublicId(),
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.ParentScopeId,
Type: resource.Host,
Pin: item.GetCatalogId(),
Id: item.GetPublicId(),
}
res.Id = item.GetPublicId()
idActions := idActionsTypeMap[globals.ResourceInfoFromPrefix(res.Id).Subtype]
@@ -0,0 +1,126 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package managed_groups_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/ldap"
"github.com/hashicorp/boundary/internal/auth/oidc"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/managed_groups"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
rw := db.New(conn)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
oidcRepoFn := func() (*oidc.Repository, error) {
// Use a small limit to test that membership lookup is explicitly unlimited
return oidc.NewRepository(ctx, rw, rw, kmsCache, oidc.WithLimit(1))
}
ldapRepoFn := func() (*ldap.Repository, error) {
// Use a small limit to test that membership lookup is explicitly unlimited
return ldap.NewRepository(ctx, rw, rw, kmsCache, ldap.WithLimit(ctx, 1))
}
s, err := managed_groups.NewService(ctx, oidcRepoFn, ldapRepoFn, 1000)
require.NoError(t, err)
org, _ := iam.TestScopes(t, iamRepo)
globalDBWrapper, err := kmsCache.GetWrapper(ctx, globals.GlobalPrefix, kms.KeyPurposeDatabase)
require.NoError(t, err)
globalOidcAm := oidc.TestAuthMethod(
t, conn, globalDBWrapper, globals.GlobalPrefix, oidc.ActivePrivateState,
"alice-rp", "fido",
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]),
oidc.WithSigningAlgs(oidc.RS256),
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
)
_ = oidc.TestAccount(t, conn, globalOidcAm, "test-subject-1", oidc.WithName("global-1"), oidc.WithDescription("global-1"))
_ = oidc.TestAccount(t, conn, globalOidcAm, "test-subject-2", oidc.WithName("global-2"), oidc.WithDescription("global-2"))
globalMg1 := oidc.TestManagedGroup(t, conn, globalOidcAm, oidc.TestFakeManagedGroupFilter)
globalMg2 := oidc.TestManagedGroup(t, conn, globalOidcAm, oidc.TestFakeManagedGroupFilter)
orgDBWrapper, err := kmsCache.GetWrapper(ctx, org.PublicId, kms.KeyPurposeDatabase)
require.NoError(t, err)
orgLdapAm := ldap.TestAuthMethod(t, conn, orgDBWrapper, org.PublicId, []string{"ldaps://ldap1"}, ldap.WithName(ctx, "global"), ldap.WithDescription(ctx, "global"))
_ = ldap.TestAccount(t, conn, orgLdapAm, "test-login-name-1", ldap.WithMemberOfGroups(ctx, "admin"), ldap.WithName(ctx, "org-1"), ldap.WithDescription(ctx, "org-1"))
_ = ldap.TestAccount(t, conn, orgLdapAm, "test-login-name-2", ldap.WithMemberOfGroups(ctx, "admin"), ldap.WithName(ctx, "org-2"), ldap.WithDescription(ctx, "org-2"))
orgMg := ldap.TestManagedGroup(t, conn, orgLdapAm, []string{"admin", "users"})
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListManagedGroupsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created oidc managed groups",
input: &pbs.ListManagedGroupsRequest{
AuthMethodId: globalOidcAm.PublicId,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=managed-group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeDescendants},
},
},
wantErr: nil,
wantIDs: []string{globalMg1.PublicId, globalMg2.PublicId},
},
{
name: "org role grant this returns all created ldap managed groups",
input: &pbs.ListManagedGroupsRequest{
AuthMethodId: orgLdapAm.PublicId,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org.PublicId,
GrantStrings: []string{"ids=*;type=managed-group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
orgMg.PublicId,
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListManagedGroups(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -998,9 +998,10 @@ func validateListRequest(ctx context.Context, req *pbs.ListManagedGroupsRequest)
func newOutputOpts(ctx context.Context, item auth.ManagedGroup, authMethodId string, authResults requestauth.VerifyResults) ([]handlers.Option, bool) {
res := perms.Resource{
ScopeId: authResults.Scope.Id,
Type: resource.ManagedGroup,
Pin: authMethodId,
ScopeId: authResults.Scope.Id,
ParentScopeId: authResults.Scope.ParentScopeId,
Type: resource.ManagedGroup,
Pin: authMethodId,
}
res.Id = item.GetPublicId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions[globals.ResourceInfoFromPrefix(item.GetPublicId()).Subtype], requestauth.WithResource(&res)).Strings()
@@ -0,0 +1,181 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package roles_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/roles"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// - project level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// - project
// Scopes [resource]:
// - global [globalRole]
// - org1 [org1Role]
// - proj1 [proj1Role]
// - org2 [org2Role]
// - proj2 [proj2Role]
// - proj3 [proj3Role]
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
kmsCache := kms.TestKms(t, conn, wrap)
s, err := roles.NewService(ctx, repoFn, 1000)
require.NoError(t, err)
org1, _ := iam.TestScopes(t, iamRepo)
org2, proj2 := iam.TestScopes(t, iamRepo)
proj3 := iam.TestProject(t, iamRepo, org2.PublicId)
var defaultOrg1Roles []string
org1Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, org1.GetPublicId()), &pbs.ListRolesRequest{
ScopeId: org1.GetPublicId(),
})
require.NoError(t, err)
for _, r := range org1Roles.Items {
defaultOrg1Roles = append(defaultOrg1Roles, r.GetId())
}
org2Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, org2.GetPublicId()), &pbs.ListRolesRequest{
ScopeId: org2.GetPublicId(),
})
require.NoError(t, err)
var defaultOrg2Roles []string
for _, r := range org2Roles.Items {
defaultOrg2Roles = append(defaultOrg2Roles, r.GetId())
}
proj2Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, proj2.GetPublicId()), &pbs.ListRolesRequest{
ScopeId: proj2.GetPublicId(),
})
require.NoError(t, err)
var defaultProj2Roles []string
for _, r := range proj2Roles.Items {
defaultProj2Roles = append(defaultProj2Roles, r.GetId())
}
proj3Roles, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, proj3.GetPublicId()), &pbs.ListRolesRequest{
ScopeId: proj3.GetPublicId(),
})
require.NoError(t, err)
var defaultProj3Roles []string
for _, r := range proj3Roles.Items {
defaultProj3Roles = append(defaultProj3Roles, r.GetId())
}
org1Role := iam.TestRole(t, conn, org1.GetPublicId(), iam.WithDescription("org1"), iam.WithName("org1"))
org2Role := iam.TestRole(t, conn, org2.GetPublicId(), iam.WithDescription("org2"), iam.WithName("org2"))
proj2Role := iam.TestRole(t, conn, proj2.GetPublicId(), iam.WithDescription("proj2"), iam.WithName("proj2"))
proj3Role := iam.TestRole(t, conn, proj3.GetPublicId(), iam.WithDescription("proj3"), iam.WithName("proj3"))
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListRolesRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
addRolesAtThisScope bool
wantIDs []string
}{
{
name: "global role grant this and children returns global and org roles",
input: &pbs.ListRolesRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=role;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
addRolesAtThisScope: true,
wantErr: nil,
wantIDs: append(append([]string{
org1Role.PublicId,
org2Role.PublicId,
}, defaultOrg1Roles...), defaultOrg2Roles...),
},
{
name: "org role grant this and children returns org and project roles",
input: &pbs.ListRolesRequest{
ScopeId: org2.PublicId,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org2.PublicId,
GrantStrings: []string{"ids=*;type=role;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
addRolesAtThisScope: true,
wantErr: nil,
wantIDs: append(append([]string{
proj2Role.PublicId,
proj3Role.PublicId,
}, defaultProj2Roles...), defaultProj3Roles...),
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
// TestAuthTokenWithRoles creates a default role, so we need to add it to the expected list
// if the grant scope contains 'this'
// This will add the default roles to the expected list of roles
if tc.addRolesAtThisScope {
var rolesAtThisScope []string
rolesAtThisScopeList, err := s.ListRoles(auth.DisabledAuthTestContext(repoFn, tc.input.ScopeId), &pbs.ListRolesRequest{
ScopeId: tc.input.ScopeId,
})
require.NoError(t, err)
for _, r := range rolesAtThisScopeList.Items {
rolesAtThisScope = append(rolesAtThisScope, r.GetId())
}
tc.wantIDs = append(tc.wantIDs, rolesAtThisScope...)
}
got, finalErr := s.ListRoles(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -1521,6 +1521,7 @@ func newOutputOpts(ctx context.Context, item *iam.Role, scopeInfoMap map[string]
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
if len(authorizedActions) == 0 {
return nil, false
@@ -0,0 +1,112 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package scopes_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/scopes"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// Scopes [resource]:
// - global [globalScope]
// - org1
// - proj1
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
s, err := scopes.NewServiceFn(ctx, repoFn, kmsCache, 1000)
require.NoError(t, err)
org1, proj1 := iam.TestScopes(t, iamRepo)
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListScopesRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created scopes",
input: &pbs.ListScopesRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=scope;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
org1.PublicId,
proj1.PublicId,
},
},
{
name: "org role grant this returns all created scopes",
input: &pbs.ListScopesRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.PublicId,
GrantStrings: []string{"ids=*;type=scope;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
proj1.PublicId,
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListScopes(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -1138,9 +1138,10 @@ func validateDestroyKeyVersionRequest(req *pbs.DestroyKeyVersionRequest) error {
func newOutputOpts(ctx context.Context, item *iam.Scope, authResults auth.VerifyResults, scopeInfoMap map[string]*pb.ScopeInfo) ([]handlers.Option, bool, error) {
res := perms.Resource{
Type: resource.Scope,
Id: item.GetPublicId(),
ScopeId: item.GetParentId(),
Type: resource.Scope,
Id: item.GetPublicId(),
ScopeId: item.GetParentId(),
ParentScopeId: scopeInfoMap[item.GetParentId()].GetParentScopeId(),
}
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), idActionsById(item.GetPublicId()), auth.WithResource(&res)).Strings()
@@ -1983,6 +1983,7 @@ func newOutputOpts(ctx context.Context, item target.Target, authResults auth.Ver
if outputFields.Has(globals.ScopeField) {
outputOpts = append(outputOpts, handlers.WithScope(authzScopes[item.GetProjectId()]))
}
pr.ParentScopeId = authzScopes[item.GetProjectId()].GetParentScopeId()
if outputFields.Has(globals.AuthorizedActionsField) {
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&pr)).Strings()
outputOpts = append(outputOpts, handlers.WithAuthorizedActions(authorizedActions))
@@ -0,0 +1,107 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package tcp_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/target"
"github.com/hashicorp/boundary/internal/target/tcp"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// Scopes [resource]:
// - org1
// - proj1
// - proj2
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
kms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
s, err := testService(t, ctx, conn, kms, wrapper)
require.NoError(t, err)
org1, proj1 := iam.TestScopes(t, iamRepo)
_, proj2 := iam.TestScopes(t, iamRepo)
target1 := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test address-1", target.WithAddress("8.8.8.8"))
target2 := tcp.TestTarget(ctx, t, conn, proj1.GetPublicId(), "test address-2", target.WithAddress("8.8.8.8"))
target3 := tcp.TestTarget(ctx, t, conn, proj2.GetPublicId(), "test address-2", target.WithAddress("8.8.8.8"))
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListTargetsRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "org role grant this returns all created targets",
input: &pbs.ListTargetsRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.GetPublicId(),
GrantStrings: []string{"ids=*;type=target;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{target1.GetPublicId(), target2.GetPublicId()},
},
{
name: "project role grant this returns all created targets",
input: &pbs.ListTargetsRequest{
ScopeId: proj2.GetPublicId(),
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: proj2.GetPublicId(),
GrantStrings: []string{"ids=*;type=target;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{target3.GetPublicId()},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kms, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
got, finalErr := s.ListTargets(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -0,0 +1,312 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package users_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
talias "github.com/hashicorp/boundary/internal/alias/target"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/users"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/stretchr/testify/require"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// - org level
// Grant - what IAM grant scope is set for the permission
// - global: descendant
// - org: children
// Scopes [resource]:
// - global [globalUser]
// - org1 [org1User]
// - org1User1
// - org2 [org2User]
// - org2User1
// - org2User2
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
wrap := db.TestWrapper(t)
iamRepo := iam.TestRepo(t, conn, wrap)
kmsCache := kms.TestKms(t, conn, wrap)
repoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
aliasRepoFn := func() (*talias.Repository, error) {
return talias.NewRepository(context.Background(), rw, rw, kmsCache)
}
s, err := users.NewService(ctx, repoFn, aliasRepoFn, 1000)
require.NoError(t, err)
org1, _ := iam.TestScopes(t, iamRepo)
org2, _ := iam.TestScopes(t, iamRepo)
globalUser := iam.TestUser(t, iamRepo, globals.GlobalPrefix, iam.WithDescription("global"), iam.WithName("global"))
org1User1 := iam.TestUser(t, iamRepo, org1.GetPublicId(), iam.WithDescription("org1"), iam.WithName("org1"))
org2User1 := iam.TestUser(t, iamRepo, org2.GetPublicId(), iam.WithDescription("org2-1"), iam.WithName("org2-1"))
org2User2 := iam.TestUser(t, iamRepo, org2.GetPublicId(), iam.WithDescription("org2-2"), iam.WithName("org2-2"))
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListUsersRequest
// set this flag when the listing user has permission to list global
// AND the test attempts to list users at global scope
// users which gets created as a part of token generation
includeTestUsers bool
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this and children recursive list at global returns global and org users",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
includeTestUsers: true,
wantErr: nil,
wantIDs: []string{
globals.AnyAuthenticatedUserId,
globals.AnonymousUserId,
globals.RecoveryUserId,
globalUser.PublicId,
org1User1.PublicId,
org2User1.PublicId,
org2User2.PublicId,
},
},
{
name: "org role grant this and children recursive list at org returns org user",
input: &pbs.ListUsersRequest{
ScopeId: org2.PublicId,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org2.PublicId,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis, globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
org2User1.PublicId,
org2User2.PublicId,
},
},
{
name: "global role grant children recursive list at org returns org user",
input: &pbs.ListUsersRequest{
ScopeId: org2.PublicId,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
org2User1.PublicId,
org2User2.PublicId,
},
},
{
name: "global role grant children recursive list at global returns all org users",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantErr: nil,
wantIDs: []string{
org1User1.PublicId,
org2User1.PublicId,
org2User2.PublicId,
},
},
{
name: "org1 role grant this recursive list at global returns org1 users",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.PublicId,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{
org1User1.PublicId,
},
},
{
name: "individual org grant this recursive list at global returns org user",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org1.PublicId,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
{
RoleScopeID: org2.PublicId,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{
org1User1.PublicId,
org2User1.PublicId,
org2User2.PublicId,
},
},
{
name: "global role grant this recursive list at org returns no user",
input: &pbs.ListUsersRequest{
ScopeId: org2.PublicId,
Recursive: true,
},
includeTestUsers: false,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{},
},
{
name: "global role grant this recursive list at global returns only global user",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
includeTestUsers: true,
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{
globals.AnyAuthenticatedUserId,
globals.AnonymousUserId,
globals.RecoveryUserId,
globalUser.PublicId,
},
},
{
name: "global role grant children non-recursive list at global returns forbidden error",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: false,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantErr: handlers.ForbiddenError(),
wantIDs: nil,
},
{
name: "org role grant children non-recursive list at global returns forbidden error",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: false,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: org2.PublicId,
GrantStrings: []string{"ids=*;type=user;actions=list,read"},
GrantScopes: []string{globals.GrantScopeChildren},
},
},
wantErr: handlers.ForbiddenError(),
wantIDs: nil,
},
{
name: "no grant recursive list returns forbidden error",
input: &pbs.ListUsersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{},
wantErr: handlers.ForbiddenError(),
wantIDs: nil,
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kmsCache, globals.GlobalPrefix, tc.rolesToCreate)
t.Cleanup(func() {
// deleting user to keep assertions clean since we're listing users over and over
_, _ = iamRepo.DeleteUser(ctx, tok.IamUserId)
})
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrap, tok, iamRepo)
got, finalErr := s.ListUsers(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
if tc.includeTestUsers {
tc.wantIDs = append(tc.wantIDs, tok.IamUserId)
}
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -1067,6 +1067,7 @@ func newOutputOpts(ctx context.Context, item *iam.User, scopeInfoMap map[string]
}
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res))
if len(authorizedActions) == 0 {
return nil, false
@@ -0,0 +1,127 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package workers_test
import (
"context"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken"
"github.com/hashicorp/boundary/internal/daemon/controller/auth"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers/workers"
"github.com/hashicorp/boundary/internal/db"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/server"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
)
// TestGrants_ReadActions tests read actions to assert that grants are being applied properly
//
// Role - which scope the role is created in
// - global level
// Scopes [resource]:
// - globalWorker1
// - globalWorker2
func TestGrants_ReadActions(t *testing.T) {
ctx := context.Background()
conn, _ := db.TestSetup(t, "postgres")
wrapper := db.TestWrapper(t)
rw := db.New(conn)
kms := kms.TestKms(t, conn, wrapper)
iamRepo := iam.TestRepo(t, conn, wrapper)
iamRepoFn := func() (*iam.Repository, error) {
return iamRepo, nil
}
repoFn := func() (*server.Repository, error) {
return server.NewRepository(ctx, rw, rw, kms)
}
workerAuthRepo, err := server.NewRepositoryStorage(ctx, rw, rw, kms)
require.NoError(t, err)
workerAuthRepoFn := func() (*server.WorkerAuthRepositoryStorage, error) {
return workerAuthRepo, nil
}
s, err := workers.NewService(context.Background(), repoFn, iamRepoFn, workerAuthRepoFn, nil)
require.NoError(t, err)
globalWorker1 := server.TestPkiWorker(t, conn, wrapper,
server.WithName("worker-1"),
server.WithDescription("worker-1"),
server.WithNewIdFunc(func(ctx context.Context) (string, error) {
return server.NewWorkerIdFromScopeAndName(ctx, scope.Global.String(), "worker-1")
}),
)
globalWorker2 := server.TestPkiWorker(t, conn, wrapper,
server.WithName("worker-2"),
server.WithDescription("worker-2"),
server.WithNewIdFunc(func(ctx context.Context) (string, error) {
return server.NewWorkerIdFromScopeAndName(ctx, scope.Global.String(), "worker-2")
}),
)
t.Run("List", func(t *testing.T) {
testcases := []struct {
name string
input *pbs.ListWorkersRequest
rolesToCreate []authtoken.TestRoleGrantsForToken
wantErr error
wantIDs []string
}{
{
name: "global role grant this returns all created workers",
input: &pbs.ListWorkersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=worker;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: nil,
wantIDs: []string{globalWorker1.PublicId, globalWorker2.PublicId},
},
{
name: "global role grant this with a non-applicable type throws an error",
input: &pbs.ListWorkersRequest{
ScopeId: globals.GlobalPrefix,
Recursive: true,
},
rolesToCreate: []authtoken.TestRoleGrantsForToken{
{
RoleScopeID: globals.GlobalPrefix,
GrantStrings: []string{"ids=*;type=group;actions=list,read"},
GrantScopes: []string{globals.GrantScopeThis},
},
},
wantErr: handlers.ApiErrorWithCode(codes.PermissionDenied),
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
tok := authtoken.TestAuthTokenWithRoles(t, conn, kms, globals.GlobalPrefix, tc.rolesToCreate)
fullGrantAuthCtx := auth.TestAuthContextFromToken(t, conn, wrapper, tok, iamRepo)
got, finalErr := s.ListWorkers(fullGrantAuthCtx, tc.input)
if tc.wantErr != nil {
require.ErrorIs(t, finalErr, tc.wantErr)
return
}
require.NoError(t, finalErr)
var gotIDs []string
for _, g := range got.Items {
gotIDs = append(gotIDs, g.GetId())
}
require.ElementsMatch(t, tc.wantIDs, gotIDs)
})
}
})
}
@@ -167,6 +167,7 @@ func (s Service) ListWorkers(ctx context.Context, req *pbs.ListWorkersRequest) (
for _, item := range ul {
res.Id = item.GetPublicId()
res.ScopeId = item.GetScopeId()
res.ParentScopeId = scopeInfoMap[item.GetScopeId()].GetParentScopeId()
authorizedActions := authResults.FetchActionSetForId(ctx, item.GetPublicId(), IdActions, auth.WithResource(&res)).Strings()
if len(authorizedActions) == 0 {
continue
+30
View File
@@ -223,6 +223,36 @@ func TestRole(t testing.TB, conn *db.DB, scopeId string, opt ...Option) *Role {
return role
}
// TestRoleWithGrants creates a role suitable for testing along with grants
// Functional options for GrantScopeIDs aren't used to express that
// this function does not provide any default grant scope unlike TestRole
func TestRoleWithGrants(t testing.TB, conn *db.DB, scopeId string, grantScopeIDs []string, grants []string) *Role {
t.Helper()
ctx := context.Background()
require := require.New(t)
rw := db.New(conn)
role, err := NewRole(ctx, scopeId)
require.NoError(err)
id, err := newRoleId(ctx)
require.NoError(err)
role.PublicId = id
require.NoError(rw.Create(ctx, role))
require.NotEmpty(role.PublicId)
for _, gsi := range grantScopeIDs {
gs, err := NewRoleGrantScope(ctx, id, gsi)
require.NoError(err)
require.NoError(rw.Create(ctx, gs))
role.GrantScopes = append(role.GrantScopes, gs)
}
for _, g := range grants {
_ = TestRoleGrant(t, conn, role.PublicId, g)
}
return role
}
func TestRoleGrant(t testing.TB, conn *db.DB, roleId, grant string, opt ...Option) *RoleGrant {
t.Helper()
require := require.New(t)