fix(uac): async SnapshotRaw data not filtered by UAC (#1540)

This commit is contained in:
LP B
2026-01-13 17:17:06 +01:00
committed by GitHub
parent a889d57013
commit b162814bd9
31 changed files with 1166 additions and 114 deletions
@@ -1,37 +0,0 @@
package utils
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/slicesx"
)
// filterByResourceControl filters a list of items based on the user's role and the resource control associated to the item.
func FilterByResourceControl[T any](tx dataservices.DataStoreTx, items []T, rcType portainer.ResourceControlType, securityContext *security.RestrictedRequestContext, idGetter func(T) string) ([]T, error) {
if securityContext.IsAdmin {
return items, nil
}
userTeamIDs := slicesx.Map(securityContext.UserMemberships, func(membership portainer.TeamMembership) portainer.TeamID {
return membership.TeamID
})
filteredItems := make([]T, 0)
for _, item := range items {
resourceControl, err := tx.ResourceControl().ResourceControlByResourceIDAndType(idGetter(item), portainer.ContainerResourceControl)
if err != nil {
return nil, fmt.Errorf("Unable to retrieve resource control: %w", err)
}
if resourceControl == nil || authorization.UserCanAccessResource(securityContext.UserID, userTeamIDs, resourceControl) {
filteredItems = append(filteredItems, item)
}
}
return filteredItems, nil
}
+11 -3
View File
@@ -9,6 +9,7 @@ import (
"github.com/portainer/portainer/api/dataservices"
dockerconsts "github.com/portainer/portainer/api/docker/consts"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/uac"
)
type StackViewModel struct {
@@ -27,6 +28,11 @@ func GetDockerStacks(tx dataservices.DataStoreTx, securityContext *security.Rest
return nil, fmt.Errorf("Unable to retrieve stacks: %w", err)
}
user, err := tx.User().Read(securityContext.UserID)
if err != nil {
return nil, fmt.Errorf("Unable to retrieve user: %w", err)
}
stacksNameSet := map[string]*StackViewModel{}
for i := range stacks {
@@ -71,9 +77,11 @@ func GetDockerStacks(tx dataservices.DataStoreTx, securityContext *security.Rest
stacksList = append(stacksList, *stack)
}
return FilterByResourceControl(tx, stacksList, portainer.StackResourceControl, securityContext, func(c StackViewModel) string {
return c.Name
})
return uac.FilterByResourceControl(stacksList, user, securityContext.UserMemberships,
func(item StackViewModel) (*portainer.ResourceControl, error) {
return uac.StackResourceControlGetter(tx, environmentID)(*item.InternalStack)
},
)
}
func isHiddenStack(labels map[string]string) bool {
@@ -6,15 +6,18 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
dockerconsts "github.com/portainer/portainer/api/docker/consts"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHandler_getDockerStacks(t *testing.T) {
is := require.New(t)
environment := &portainer.Endpoint{
ID: 1,
SecuritySettings: portainer.EndpointSecuritySettings{
@@ -54,44 +57,52 @@ func TestHandler_getDockerStacks(t *testing.T) {
Type: portainer.DockerComposeStack,
}
datastore := testhelpers.NewDatastore(
testhelpers.WithEndpoints([]portainer.Endpoint{*environment}),
testhelpers.WithStacks([]portainer.Stack{
stack1,
ok, store := datastore.MustNewTestStore(t, true, false)
is.True(ok)
is.NoError(store.UpdateTx(func(tx dataservices.DataStoreTx) error {
is.NoError(tx.Endpoint().Create(environment))
is.NoError(tx.Stack().Create(&stack1))
is.NoError(tx.Stack().Create(&portainer.Stack{
ID: 2,
Name: "stack2",
EndpointID: 2,
Type: portainer.DockerSwarmStack,
}))
is.NoError(tx.User().Create(&portainer.User{ID: 1, Role: portainer.AdministratorRole}))
return nil
}))
is.NoError(store.ViewTx(func(tx dataservices.DataStoreTx) error {
stacksList, err := GetDockerStacks(tx, &security.RestrictedRequestContext{
IsAdmin: true,
UserID: 1,
}, environment.ID, containers, services)
require.NoError(t, err)
assert.Len(t, stacksList, 3)
expectedStacks := []StackViewModel{
{
InternalStack: &stack1,
ID: 1,
Name: "stack1",
IsExternal: false,
Type: portainer.DockerComposeStack,
},
{
ID: 2,
Name: "stack2",
EndpointID: 2,
IsExternal: true,
Type: portainer.DockerComposeStack,
},
{
Name: "stack3",
IsExternal: true,
Type: portainer.DockerSwarmStack,
},
}),
)
}
stacksList, err := GetDockerStacks(datastore, &security.RestrictedRequestContext{
IsAdmin: true,
}, environment.ID, containers, services)
require.NoError(t, err)
assert.Len(t, stacksList, 3)
assert.ElementsMatch(t, expectedStacks, stacksList)
return nil
}))
expectedStacks := []StackViewModel{
{
InternalStack: &stack1,
ID: 1,
Name: "stack1",
IsExternal: false,
Type: portainer.DockerComposeStack,
},
{
Name: "stack2",
IsExternal: true,
Type: portainer.DockerComposeStack,
},
{
Name: "stack3",
IsExternal: true,
Type: portainer.DockerSwarmStack,
},
}
assert.ElementsMatch(t, expectedStacks, stacksList)
}