Files
threshold/internal/daemon/controller/handlers/authmethods/grants_test.go
T
7c4451dc73 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>
2025-01-24 10:23:33 -08:00

267 lines
9.1 KiB
Go

// 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)
}
})
}
})
}