mirror of https://github.com/portainer/portainer
80 lines
3.0 KiB
Go
80 lines
3.0 KiB
Go
package proxy
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/portainer/portainer"
|
|
)
|
|
|
|
const (
|
|
// ErrDockerTaskServiceIdentifierNotFound defines an error raised when Portainer is unable to find the service identifier associated to a task
|
|
ErrDockerTaskServiceIdentifierNotFound = portainer.Error("Docker task service identifier not found")
|
|
taskServiceIdentifier = "ServiceID"
|
|
taskLabelForStackIdentifier = "com.docker.stack.namespace"
|
|
)
|
|
|
|
// taskListOperation extracts the response as a JSON object, loop through the tasks array
|
|
// and filter the tasks based on resource controls before rewriting the response
|
|
func taskListOperation(response *http.Response, executor *operationExecutor) error {
|
|
var err error
|
|
|
|
// TaskList response is a JSON array
|
|
// https://docs.docker.com/engine/api/v1.28/#operation/TaskList
|
|
responseArray, err := getResponseAsJSONArray(response)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !executor.operationContext.isAdmin {
|
|
responseArray, err = filterTaskList(responseArray, executor.operationContext)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return rewriteResponse(response, responseArray, http.StatusOK)
|
|
}
|
|
|
|
// extractTaskLabelsFromTaskListObject retrieve the Labels of the task if present.
|
|
// Task schema reference: https://docs.docker.com/engine/api/v1.28/#operation/TaskList
|
|
func extractTaskLabelsFromTaskListObject(responseObject map[string]interface{}) map[string]interface{} {
|
|
// Labels are stored under Spec.ContainerSpec.Labels
|
|
taskSpecObject := extractJSONField(responseObject, "Spec")
|
|
if taskSpecObject != nil {
|
|
containerSpecObject := extractJSONField(taskSpecObject, "ContainerSpec")
|
|
if containerSpecObject != nil {
|
|
return extractJSONField(containerSpecObject, "Labels")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// filterTaskList loops through all tasks and filters public tasks (no associated resource control)
|
|
// as well as authorized tasks (access granted to the user based on existing resource control).
|
|
// Resource controls checks are based on: service identifier, stack identifier (from label).
|
|
// Task object schema reference: https://docs.docker.com/engine/api/v1.28/#operation/TaskList
|
|
// any resource control giving access to the user based on the associated service identifier.
|
|
func filterTaskList(taskData []interface{}, context *restrictedOperationContext) ([]interface{}, error) {
|
|
filteredTaskData := make([]interface{}, 0)
|
|
|
|
for _, task := range taskData {
|
|
taskObject := task.(map[string]interface{})
|
|
if taskObject[taskServiceIdentifier] == nil {
|
|
return nil, ErrDockerTaskServiceIdentifierNotFound
|
|
}
|
|
|
|
serviceID := taskObject[taskServiceIdentifier].(string)
|
|
taskObject, access := applyResourceAccessControl(taskObject, serviceID, context)
|
|
if !access {
|
|
taskLabels := extractTaskLabelsFromTaskListObject(taskObject)
|
|
taskObject, access = applyResourceAccessControlFromLabel(taskLabels, taskObject, taskLabelForStackIdentifier, context)
|
|
}
|
|
|
|
if access {
|
|
filteredTaskData = append(filteredTaskData, taskObject)
|
|
}
|
|
}
|
|
|
|
return filteredTaskData, nil
|
|
}
|