hunk.code_window: 	err = <-errorChan
   	if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNoStatusReceived) {
   		log.Printf("websocket error: %s \n", err.Error())
  
hunk.labels: keep add keep keep
hunk.after_edit: 	// websocket client successfully disconnected
  
hunk.file_path: api/http/handler/websocket/pod.go
hunk.type: add
file: package docker
  
  import (
  	"context"
  	"net/http"
  
  	"github.com/docker/docker/client"
  
  	portainer "github.com/portainer/portainer/api"
  	"github.com/portainer/portainer/api/http/proxy/factory/utils"
  	"github.com/portainer/portainer/api/internal/authorization"
  )
  
  const (
  	configObjectIdentifier = "ID"
  )
  
  func getInheritedResourceControlFromConfigLabels(dockerClient *client.Client, endpointID portainer.EndpointID, configID string, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) {
  	config, _, err := dockerClient.ConfigInspectWithRaw(context.Background(), configID)
  	if err != nil {
  		return nil, err
  	}
  
  	stackResourceID := getStackResourceIDFromLabels(config.Spec.Labels, endpointID)
  	if stackResourceID != "" {
  		return authorization.GetResourceControlByResourceIDAndType(stackResourceID, portainer.StackResourceControl, resourceControls), nil
  	}
  
  	return nil, nil
  }
  
  // configListOperation extracts the response as a JSON object, loop through the configs array
  // decorate and/or filter the configs based on resource controls before rewriting the response.
  func (transport *Transport) configListOperation(response *http.Response, executor *operationExecutor) error {
  	// ConfigList response is a JSON array
  	// https://docs.docker.com/engine/api/v1.30/#operation/ConfigList
  	responseArray, err := utils.GetResponseAsJSONArray(response)
  	if err != nil {
  		return err
  	}
  
  	resourceOperationParameters := &resourceOperationParameters{
  		resourceIdentifierAttribute: configObjectIdentifier,
  		resourceType:                portainer.ConfigResourceControl,
  		labelsObjectSelector:        selectorConfigLabels,
  	}
  
  	responseArray, err = transport.applyAccessControlOnResourceList(resourceOperationParameters, responseArray, executor)
  	if err != nil {
  		return err
  	}
  
  	return utils.RewriteResponse(response, responseArray, http.StatusOK)
  }
  
  // configInspectOperation extracts the response as a JSON object, verify that the user
  // has access to the config based on resource control and either rewrite an access denied response or a decorated config.
  func (transport *Transport) configInspectOperation(response *http.Response, executor *operationExecutor) error {
  	// ConfigInspect response is a JSON object
  	// https://docs.docker.com/engine/api/v1.30/#operation/ConfigInspect
  	responseObject, err := utils.GetResponseAsJSONObject(response)
  	if err != nil {
  		return err
  	}
  
  	resourceOperationParameters := &resourceOperationParameters{
  		resourceIdentifierAttribute: configObjectIdentifier,
  		resourceType:                portainer.ConfigResourceControl,
  		labelsObjectSelector:        selectorConfigLabels,
  	}
  
  	return transport.applyAccessControlOnResource(resourceOperationParameters, responseObject, response, executor)
  }
  
  // selectorConfigLabels retrieve the labels object associated to the config object.
  // Labels are available under the "Spec.Labels" property.
  // API schema references:
  // https://docs.docker.com/engine/api/v1.37/#operation/ConfigList
  // https://docs.docker.com/engine/api/v1.37/#operation/ConfigInspect
  func selectorConfigLabels(responseObject map[string]interface{}) map[string]interface{} {
  	secretSpec := utils.GetJSONObject(responseObject, "Spec")
  	if secretSpec != nil {
  		secretLabelsObject := utils.GetJSONObject(secretSpec, "Labels")
  		return secretLabelsObject
  	}
  	return nil
  }
  