2017-09-19 18:23:48 +00:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
|
2019-03-21 01:20:14 +00:00
|
|
|
"github.com/portainer/portainer/api"
|
2017-09-19 18:23:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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"
|
2017-10-15 17:24:40 +00:00
|
|
|
taskLabelForStackIdentifier = "com.docker.stack.namespace"
|
2017-09-19 18:23:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2018-05-06 07:15:57 +00:00
|
|
|
func taskListOperation(response *http.Response, executor *operationExecutor) error {
|
2017-09-19 18:23:48 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-05-24 06:04:58 +00:00
|
|
|
if !executor.operationContext.isAdmin && !executor.operationContext.endpointResourceAccess {
|
2017-10-15 17:24:40 +00:00
|
|
|
responseArray, err = filterTaskList(responseArray, executor.operationContext)
|
2017-09-19 18:23:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rewriteResponse(response, responseArray, http.StatusOK)
|
|
|
|
}
|
2017-10-15 17:24:40 +00:00
|
|
|
|
|
|
|
// 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.
|
2019-05-24 06:04:58 +00:00
|
|
|
func filterTaskList(taskData []interface{}, context *restrictedDockerOperationContext) ([]interface{}, error) {
|
2017-10-15 17:24:40 +00:00
|
|
|
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)
|
2018-08-19 05:57:28 +00:00
|
|
|
if !access {
|
2017-10-15 17:24:40 +00:00
|
|
|
taskLabels := extractTaskLabelsFromTaskListObject(taskObject)
|
|
|
|
taskObject, access = applyResourceAccessControlFromLabel(taskLabels, taskObject, taskLabelForStackIdentifier, context)
|
2018-08-19 05:57:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if access {
|
|
|
|
filteredTaskData = append(filteredTaskData, taskObject)
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filteredTaskData, nil
|
|
|
|
}
|