mirror of
https://github.com/portainer/portainer.git
synced 2025-07-25 08:19:40 +02:00
chore(code): clean up the code EE-7251 (#11948)
Some checks are pending
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
This commit is contained in:
parent
be9d3285e1
commit
bfa27d9103
49 changed files with 338 additions and 368 deletions
|
@ -90,7 +90,7 @@ func singleAPIRequest(h *Handler, jwt string, is *assert.Assertions, expect stri
|
|||
FileContent string
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBuffer([]byte("{}")))
|
||||
req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBufferString("{}"))
|
||||
testhelpers.AddTestSecurityCookie(req, jwt)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
@ -196,7 +196,7 @@ func Test_customTemplateGitFetch(t *testing.T) {
|
|||
}
|
||||
h := NewHandler(requestBouncer, store, fileService, invalidGitService)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBuffer([]byte("{}")))
|
||||
req := httptest.NewRequest(http.MethodPut, "/custom_templates/1/git_fetch", bytes.NewBufferString("{}"))
|
||||
testhelpers.AddTestSecurityCookie(req, jwt1)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
@ -30,8 +31,7 @@ func (handler *Handler) recreate(w http.ResponseWriter, r *http.Request) *httper
|
|||
}
|
||||
|
||||
var payload RecreatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -40,8 +40,7 @@ func (handler *Handler) recreate(w http.ResponseWriter, r *http.Request) *httper
|
|||
return httperror.NotFound("Unable to find an environment on request context", err)
|
||||
}
|
||||
|
||||
err = handler.bouncer.AuthorizedEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.bouncer.AuthorizedEndpointOperation(r, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to force update service", err)
|
||||
}
|
||||
|
||||
|
@ -58,8 +57,9 @@ func (handler *Handler) recreate(w http.ResponseWriter, r *http.Request) *httper
|
|||
go func() {
|
||||
images.EvictImageStatus(containerID)
|
||||
images.EvictImageStatus(newContainer.Config.Labels[consts.ComposeStackNameLabel])
|
||||
images.EvictImageStatus(newContainer.Config.Labels[consts.SwarmServiceIdLabel])
|
||||
images.EvictImageStatus(newContainer.Config.Labels[consts.SwarmServiceIDLabel])
|
||||
}()
|
||||
|
||||
return response.JSON(w, newContainer)
|
||||
}
|
||||
|
||||
|
@ -67,6 +67,7 @@ func (handler *Handler) createResourceControl(oldContainerId string, newContaine
|
|||
resourceControls, err := handler.dataStore.ResourceControl().ReadAll()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Exporting Resource Controls")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -74,11 +75,10 @@ func (handler *Handler) createResourceControl(oldContainerId string, newContaine
|
|||
if resourceControl == nil {
|
||||
return
|
||||
}
|
||||
|
||||
resourceControl.ResourceID = newContainerId
|
||||
err = handler.dataStore.ResourceControl().Create(resourceControl)
|
||||
if err != nil {
|
||||
if err := handler.dataStore.ResourceControl().Create(resourceControl); err != nil {
|
||||
log.Error().Err(err).Str("containerId", newContainerId).Msg("Failed to create new resource control for container")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,12 +86,12 @@ func (handler *Handler) updateWebhook(oldContainerId string, newContainerId stri
|
|||
webhook, err := handler.dataStore.Webhook().WebhookByResourceID(oldContainerId)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("containerId", oldContainerId).Msg("cannot find webhook by containerId")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
webhook.ResourceID = newContainerId
|
||||
err = handler.dataStore.Webhook().Update(webhook.ID, webhook)
|
||||
if err != nil {
|
||||
if err := handler.dataStore.Webhook().Update(webhook.ID, webhook); err != nil {
|
||||
log.Error().Err(err).Int("webhookId", int(webhook.ID)).Msg("cannot update webhook")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
edgestackutils "github.com/portainer/portainer/api/internal/edge/edgestacks"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (handler *Handler) updateStackVersion(stack *portainer.EdgeStack, deploymentType portainer.EdgeStackDeploymentType, config []byte, oldGitHash string, relatedEnvironmentsIDs []portainer.EndpointID) error {
|
||||
|
||||
stack.Version = stack.Version + 1
|
||||
stack.Status = edgestackutils.NewStatus(stack.Status, relatedEnvironmentsIDs)
|
||||
|
||||
|
@ -19,11 +19,9 @@ func (handler *Handler) updateStackVersion(stack *portainer.EdgeStack, deploymen
|
|||
}
|
||||
|
||||
func (handler *Handler) storeStackFile(stack *portainer.EdgeStack, deploymentType portainer.EdgeStackDeploymentType, config []byte) error {
|
||||
|
||||
if deploymentType != stack.DeploymentType {
|
||||
// deployment type was changed - need to delete all old files
|
||||
err := handler.FileService.RemoveDirectory(stack.ProjectPath)
|
||||
if err != nil {
|
||||
if err := handler.FileService.RemoveDirectory(stack.ProjectPath); err != nil {
|
||||
log.Warn().Err(err).Msg("Unable to clear old files")
|
||||
}
|
||||
|
||||
|
@ -50,8 +48,7 @@ func (handler *Handler) storeStackFile(stack *portainer.EdgeStack, deploymentTyp
|
|||
entryPoint = stack.ManifestPath
|
||||
}
|
||||
|
||||
_, err := handler.FileService.StoreEdgeStackFileFromBytes(stackFolder, entryPoint, config)
|
||||
if err != nil {
|
||||
if _, err := handler.FileService.StoreEdgeStackFileFromBytes(stackFolder, entryPoint, config); err != nil {
|
||||
return fmt.Errorf("unable to persist updated Compose file with version on disk: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,11 +7,13 @@ import (
|
|||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/internal/tag"
|
||||
"github.com/portainer/portainer/api/pendingactions/handlers"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
@ -53,17 +55,17 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
var payload endpointGroupUpdatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
var endpointGroup *portainer.EndpointGroup
|
||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
|
||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
|
||||
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
var httpErr *httperror.HandlerError
|
||||
if errors.As(err, &httpErr) {
|
||||
return httpErr
|
||||
|
@ -151,25 +153,20 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
|
|||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
if endpoint.GroupID == endpointGroup.ID {
|
||||
if endpoint.Type == portainer.KubernetesLocalEnvironment || endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment {
|
||||
err = handler.AuthorizationService.CleanNAPWithOverridePolicies(tx, &endpoint, endpointGroup)
|
||||
if err != nil {
|
||||
// Update flag with endpoint and continue
|
||||
go func(endpointID portainer.EndpointID, endpointGroupID portainer.EndpointGroupID) {
|
||||
err := handler.PendingActionsService.Create(handlers.NewCleanNAPWithOverridePolicies(endpointID, &endpointGroupID))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to create pending action to clean NAP with override policies for endpoint (%d) and endpoint group (%d).", endpointID, endpointGroupID)
|
||||
}
|
||||
}(endpoint.ID, endpointGroup.ID)
|
||||
}
|
||||
if endpoint.GroupID == endpointGroup.ID && endpointutils.IsKubernetesEndpoint(&endpoint) {
|
||||
if err := handler.AuthorizationService.CleanNAPWithOverridePolicies(tx, &endpoint, endpointGroup); err != nil {
|
||||
// Update flag with endpoint and continue
|
||||
go func(endpointID portainer.EndpointID, endpointGroupID portainer.EndpointGroupID) {
|
||||
if err := handler.PendingActionsService.Create(handlers.NewCleanNAPWithOverridePolicies(endpointID, &endpointGroupID)); err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to create pending action to clean NAP with override policies for endpoint (%d) and endpoint group (%d).", endpointID, endpointGroupID)
|
||||
}
|
||||
}(endpoint.ID, endpointGroup.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.EndpointGroup().Update(endpointGroup.ID, endpointGroup)
|
||||
if err != nil {
|
||||
if err := tx.EndpointGroup().Update(endpointGroup.ID, endpointGroup); err != nil {
|
||||
return nil, httperror.InternalServerError("Unable to persist environment group changes inside the database", err)
|
||||
}
|
||||
|
||||
|
@ -177,13 +174,11 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
|
|||
endpoints, err := tx.Endpoint().Endpoints()
|
||||
if err != nil {
|
||||
return nil, httperror.InternalServerError("Unable to retrieve environments from the database", err)
|
||||
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
if endpoint.GroupID == endpointGroup.ID {
|
||||
err = handler.updateEndpointRelations(tx, &endpoint, endpointGroup)
|
||||
if err != nil {
|
||||
if err := handler.updateEndpointRelations(tx, &endpoint, endpointGroup); err != nil {
|
||||
return nil, httperror.InternalServerError("Unable to persist environment relations changes inside the database", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,10 +64,9 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
|
|||
return httperror.BadRequest("Invalid boolean query parameter", err)
|
||||
}
|
||||
|
||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
return handler.deleteEndpoint(tx, portainer.EndpointID(endpointID), deleteCluster)
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
var handlerError *httperror.HandlerError
|
||||
if errors.As(err, &handlerError) {
|
||||
return handlerError
|
||||
|
@ -87,7 +86,7 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
|
|||
// @security ApiKeyAuth || jwt
|
||||
// @accept json
|
||||
// @produce json
|
||||
// @param body body endpointDeleteBatchPayload true "List of environments to delete, with optional deleteCluster flag to clean-up assocaited resources (cloud environments only)"
|
||||
// @param body body endpointDeleteBatchPayload true "List of environments to delete, with optional deleteCluster flag to clean-up associated resources (cloud environments only)"
|
||||
// @success 204 "Environment(s) successfully deleted."
|
||||
// @failure 207 {object} endpointDeleteBatchPartialResponse "Partial success. Some environments were deleted successfully, while others failed."
|
||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||
|
@ -139,28 +138,24 @@ func (handler *Handler) deleteEndpoint(tx dataservices.DataStoreTx, endpointID p
|
|||
|
||||
if endpoint.TLSConfig.TLS {
|
||||
folder := strconv.Itoa(int(endpointID))
|
||||
err = handler.FileService.DeleteTLSFiles(folder)
|
||||
if err != nil {
|
||||
if err := handler.FileService.DeleteTLSFiles(folder); err != nil {
|
||||
log.Error().Err(err).Msgf("Unable to remove TLS files from disk when deleting endpoint %d", endpointID)
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Snapshot().Delete(endpointID)
|
||||
if err != nil {
|
||||
if err := tx.Snapshot().Delete(endpointID); err != nil {
|
||||
log.Warn().Err(err).Msgf("Unable to remove the snapshot from the database")
|
||||
}
|
||||
|
||||
handler.ProxyManager.DeleteEndpointProxy(endpoint.ID)
|
||||
|
||||
if len(endpoint.UserAccessPolicies) > 0 || len(endpoint.TeamAccessPolicies) > 0 {
|
||||
err = handler.AuthorizationService.UpdateUsersAuthorizationsTx(tx)
|
||||
if err != nil {
|
||||
if err := handler.AuthorizationService.UpdateUsersAuthorizationsTx(tx); err != nil {
|
||||
log.Warn().Err(err).Msgf("Unable to update user authorizations")
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.EndpointRelation().DeleteEndpointRelation(endpoint.ID)
|
||||
if err != nil {
|
||||
if err := tx.EndpointRelation().DeleteEndpointRelation(endpoint.ID); err != nil {
|
||||
log.Warn().Err(err).Msgf("Unable to remove environment relation from the database")
|
||||
}
|
||||
|
||||
|
@ -189,8 +184,7 @@ func (handler *Handler) deleteEndpoint(tx dataservices.DataStoreTx, endpointID p
|
|||
return e == endpoint.ID
|
||||
})
|
||||
|
||||
err = tx.EdgeGroup().Update(edgeGroup.ID, &edgeGroup)
|
||||
if err != nil {
|
||||
if err := tx.EdgeGroup().Update(edgeGroup.ID, &edgeGroup); err != nil {
|
||||
log.Warn().Err(err).Msgf("Unable to update edge group")
|
||||
}
|
||||
}
|
||||
|
@ -247,13 +241,11 @@ func (handler *Handler) deleteEndpoint(tx dataservices.DataStoreTx, endpointID p
|
|||
}
|
||||
|
||||
// delete the pending actions
|
||||
err = tx.PendingActions().DeleteByEndpointID(endpoint.ID)
|
||||
if err != nil {
|
||||
if err := tx.PendingActions().DeleteByEndpointID(endpoint.ID); err != nil {
|
||||
log.Warn().Err(err).Int("endpointId", int(endpoint.ID)).Msgf("Unable to delete pending actions")
|
||||
}
|
||||
|
||||
err = tx.Endpoint().DeleteEndpoint(endpointID)
|
||||
if err != nil {
|
||||
if err := tx.Endpoint().DeleteEndpoint(endpointID); err != nil {
|
||||
return httperror.InternalServerError("Unable to delete the environment from the database", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ func (handler *Handler) endpointForceUpdateService(w http.ResponseWriter, r *htt
|
|||
// ignore errors from this cleanup function, log them instead
|
||||
containers, err := dockerClient.ContainerList(context.TODO(), container.ListOptions{
|
||||
All: true,
|
||||
Filters: filters.NewArgs(filters.Arg("label", consts.SwarmServiceIdLabel+"="+payload.ServiceID)),
|
||||
Filters: filters.NewArgs(filters.Arg("label", consts.SwarmServiceIDLabel+"="+payload.ServiceID)),
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("Environment", endpoint.Name).Msg("Error listing containers")
|
||||
|
|
|
@ -32,8 +32,7 @@ func (handler *Handler) registryDelete(w http.ResponseWriter, r *http.Request) *
|
|||
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve info from request context", err)
|
||||
}
|
||||
if !securityContext.IsAdmin {
|
||||
} else if !securityContext.IsAdmin {
|
||||
return httperror.Forbidden("Permission denied to delete registry", httperrors.ErrResourceAccessDenied)
|
||||
}
|
||||
|
||||
|
@ -47,21 +46,16 @@ func (handler *Handler) registryDelete(w http.ResponseWriter, r *http.Request) *
|
|||
return httperror.InternalServerError(fmt.Sprintf("Unable to load registry %q from the database", registry.Name), err)
|
||||
}
|
||||
|
||||
err = handler.DataStore.Registry().Delete(portainer.RegistryID(registryID))
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Registry().Delete(portainer.RegistryID(registryID)); err != nil {
|
||||
return httperror.InternalServerError("Unable to remove the registry from the database", err)
|
||||
}
|
||||
|
||||
err = handler.deleteKubernetesSecrets(registry)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to delete registry secrets", err)
|
||||
}
|
||||
handler.deleteKubernetesSecrets(registry)
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
||||
func (handler *Handler) deleteKubernetesSecrets(registry *portainer.Registry) error {
|
||||
|
||||
func (handler *Handler) deleteKubernetesSecrets(registry *portainer.Registry) {
|
||||
for endpointId, access := range registry.RegistryAccesses {
|
||||
if access.Namespaces != nil {
|
||||
// Obtain a kubeclient for the endpoint
|
||||
|
@ -69,6 +63,7 @@ func (handler *Handler) deleteKubernetesSecrets(registry *portainer.Registry) er
|
|||
if err != nil {
|
||||
// Skip environments that can't be loaded from the DB
|
||||
log.Warn().Err(err).Msgf("Unable to load the environment with id %d from the database", endpointId)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -76,13 +71,14 @@ func (handler *Handler) deleteKubernetesSecrets(registry *portainer.Registry) er
|
|||
if err != nil {
|
||||
// Skip environments that can't get a kubeclient from
|
||||
log.Warn().Err(err).Msgf("Unable to get kubernetes client for environment %d", endpointId)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
failedNamespaces := make([]string, 0)
|
||||
|
||||
for _, ns := range access.Namespaces {
|
||||
err = cli.DeleteRegistrySecret(registry.ID, ns)
|
||||
if err != nil {
|
||||
if err := cli.DeleteRegistrySecret(registry.ID, ns); err != nil {
|
||||
failedNamespaces = append(failedNamespaces, ns)
|
||||
log.Warn().Err(err).Msgf("Unable to delete registry secret %q from namespace %q for environment %d. Retrying offline", cli.RegistrySecretName(registry.ID), ns, endpointId)
|
||||
}
|
||||
|
@ -95,6 +91,4 @@ func (handler *Handler) deleteKubernetesSecrets(registry *portainer.Registry) er
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -32,11 +32,10 @@ func (handler *Handler) resourceControlDelete(w http.ResponseWriter, r *http.Req
|
|||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a resource control with the specified identifier inside the database", err)
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find a resource control with with the specified identifier inside the database", err)
|
||||
return httperror.InternalServerError("Unable to find a resource control with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
err = handler.DataStore.ResourceControl().Delete(portainer.ResourceControlID(resourceControlID))
|
||||
if err != nil {
|
||||
if err := handler.DataStore.ResourceControl().Delete(portainer.ResourceControlID(resourceControlID)); err != nil {
|
||||
return httperror.InternalServerError("Unable to remove the resource control from the database", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ func (payload *resourceControlUpdatePayload) Validate(r *http.Request) error {
|
|||
if payload.Public && payload.AdministratorsOnly {
|
||||
return errors.New("invalid payload: cannot set public and administrators only")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -58,8 +59,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
|
||||
var payload resourceControlUpdatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req
|
|||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a resource control with the specified identifier inside the database", err)
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find a resource control with with the specified identifier inside the database", err)
|
||||
return httperror.InternalServerError("Unable to find a resource control with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
securityContext, err := security.RetrieveRestrictedRequestContext(r)
|
||||
|
@ -106,8 +106,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req
|
|||
return httperror.Forbidden("Permission denied to update the resource control", httperrors.ErrResourceAccessDenied)
|
||||
}
|
||||
|
||||
err = handler.DataStore.ResourceControl().Update(resourceControl.ID, resourceControl)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.ResourceControl().Update(resourceControl.ID, resourceControl); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist resource control changes inside the database", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ func (payload *updateComposeStackPayload) Validate(r *http.Request) error {
|
|||
if govalidator.IsNull(payload.StackFileContent) {
|
||||
return errors.New("Invalid stack file content")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -50,6 +51,7 @@ func (payload *updateSwarmStackPayload) Validate(r *http.Request) error {
|
|||
if govalidator.IsNull(payload.StackFileContent) {
|
||||
return errors.New("Invalid stack file content")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -102,8 +104,7 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
|
|||
return httperror.InternalServerError("Unable to find the environment associated to the stack inside the database", err)
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
}
|
||||
|
||||
|
@ -114,45 +115,40 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
|
|||
|
||||
//only check resource control when it is a DockerSwarmStack or a DockerComposeStack
|
||||
if stack.Type == portainer.DockerSwarmStack || stack.Type == portainer.DockerComposeStack {
|
||||
|
||||
resourceControl, err := handler.DataStore.ResourceControl().ResourceControlByResourceIDAndType(stackutils.ResourceControlID(stack.EndpointID, stack.Name), portainer.StackResourceControl)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve a resource control associated to the stack", err)
|
||||
}
|
||||
|
||||
access, err := handler.userCanAccessStack(securityContext, endpoint.ID, resourceControl)
|
||||
if err != nil {
|
||||
if access, err := handler.userCanAccessStack(securityContext, endpoint.ID, resourceControl); err != nil {
|
||||
return httperror.InternalServerError("Unable to verify user authorizations to validate stack access", err)
|
||||
}
|
||||
if !access {
|
||||
} else if !access {
|
||||
return httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied)
|
||||
}
|
||||
}
|
||||
|
||||
canManage, err := handler.userCanManageStacks(securityContext, endpoint)
|
||||
if err != nil {
|
||||
if canManage, err := handler.userCanManageStacks(securityContext, endpoint); err != nil {
|
||||
return httperror.InternalServerError("Unable to verify user authorizations to validate stack deletion", err)
|
||||
}
|
||||
if !canManage {
|
||||
} else if !canManage {
|
||||
errMsg := "Stack editing is disabled for non-admin users"
|
||||
|
||||
return httperror.Forbidden(errMsg, errors.New(errMsg))
|
||||
}
|
||||
|
||||
updateError := handler.updateAndDeployStack(r, stack, endpoint)
|
||||
if updateError != nil {
|
||||
return updateError
|
||||
if err := handler.updateAndDeployStack(r, stack, endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := handler.DataStore.User().Read(securityContext.UserID)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Cannot find context user", errors.Wrap(err, "failed to fetch the user"))
|
||||
}
|
||||
|
||||
stack.UpdatedBy = user.Username
|
||||
stack.UpdateDate = time.Now().Unix()
|
||||
stack.Status = portainer.StackStatusActive
|
||||
|
||||
err = handler.DataStore.Stack().Update(stack.ID, stack)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Stack().Update(stack.ID, stack); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the stack changes inside the database", err)
|
||||
}
|
||||
|
||||
|
@ -165,19 +161,20 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt
|
|||
}
|
||||
|
||||
func (handler *Handler) updateAndDeployStack(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint) *httperror.HandlerError {
|
||||
if stack.Type == portainer.DockerSwarmStack {
|
||||
switch stack.Type {
|
||||
case portainer.DockerSwarmStack:
|
||||
stack.Name = handler.SwarmStackManager.NormalizeStackName(stack.Name)
|
||||
|
||||
return handler.updateSwarmStack(r, stack, endpoint)
|
||||
} else if stack.Type == portainer.DockerComposeStack {
|
||||
case portainer.DockerComposeStack:
|
||||
stack.Name = handler.ComposeStackManager.NormalizeStackName(stack.Name)
|
||||
|
||||
return handler.updateComposeStack(r, stack, endpoint)
|
||||
} else if stack.Type == portainer.KubernetesStack {
|
||||
case portainer.KubernetesStack:
|
||||
return handler.updateKubernetesStack(r, stack, endpoint)
|
||||
} else {
|
||||
return httperror.InternalServerError("Unsupported stack", errors.Errorf("unsupported stack type: %v", stack.Type))
|
||||
}
|
||||
|
||||
return httperror.InternalServerError("Unsupported stack", errors.Errorf("unsupported stack type: %v", stack.Type))
|
||||
}
|
||||
|
||||
func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Stack, endpoint *portainer.Endpoint) *httperror.HandlerError {
|
||||
|
@ -191,8 +188,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
|
|||
}
|
||||
|
||||
var payload updateComposeStackPayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -204,8 +200,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
|
|||
}
|
||||
|
||||
stackFolder := strconv.Itoa(int(stack.ID))
|
||||
_, err = handler.FileService.UpdateStoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent))
|
||||
if err != nil {
|
||||
if _, err := handler.FileService.UpdateStoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent)); err != nil {
|
||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||
log.Warn().Err(rollbackErr).Msg("rollback stack file error")
|
||||
}
|
||||
|
@ -236,8 +231,7 @@ func (handler *Handler) updateComposeStack(r *http.Request, stack *portainer.Sta
|
|||
}
|
||||
|
||||
// Deploy the stack
|
||||
err = composeDeploymentConfig.Deploy()
|
||||
if err != nil {
|
||||
if err := composeDeploymentConfig.Deploy(); err != nil {
|
||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||
log.Warn().Err(rollbackErr).Msg("rollback stack file error")
|
||||
}
|
||||
|
@ -261,8 +255,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
|
|||
}
|
||||
|
||||
var payload updateSwarmStackPayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -274,8 +267,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
|
|||
}
|
||||
|
||||
stackFolder := strconv.Itoa(int(stack.ID))
|
||||
_, err = handler.FileService.UpdateStoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent))
|
||||
if err != nil {
|
||||
if _, err := handler.FileService.UpdateStoreStackFileFromBytes(stackFolder, stack.EntryPoint, []byte(payload.StackFileContent)); err != nil {
|
||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||
log.Warn().Err(rollbackErr).Msg("rollback stack file error")
|
||||
}
|
||||
|
@ -306,8 +298,7 @@ func (handler *Handler) updateSwarmStack(r *http.Request, stack *portainer.Stack
|
|||
}
|
||||
|
||||
// Deploy the stack
|
||||
err = swarmDeploymentConfig.Deploy()
|
||||
if err != nil {
|
||||
if err := swarmDeploymentConfig.Deploy(); err != nil {
|
||||
if rollbackErr := handler.FileService.RollbackStackFile(stackFolder, stack.EntryPoint); rollbackErr != nil {
|
||||
log.Warn().Err(rollbackErr).Msg("rollback stack file error")
|
||||
}
|
||||
|
|
|
@ -71,8 +71,7 @@ func (handler *Handler) templateFile(w http.ResponseWriter, r *http.Request) *ht
|
|||
|
||||
defer handler.cleanUp(projectPath)
|
||||
|
||||
err = handler.GitService.CloneRepository(projectPath, template.Repository.URL, "", "", "", false)
|
||||
if err != nil {
|
||||
if err := handler.GitService.CloneRepository(projectPath, template.Repository.URL, "", "", "", false); err != nil {
|
||||
return httperror.InternalServerError("Unable to clone git repository", err)
|
||||
}
|
||||
|
||||
|
@ -82,12 +81,10 @@ func (handler *Handler) templateFile(w http.ResponseWriter, r *http.Request) *ht
|
|||
}
|
||||
|
||||
return response.JSON(w, fileResponse{FileContent: string(fileContent)})
|
||||
|
||||
}
|
||||
|
||||
func (handler *Handler) cleanUp(projectPath string) {
|
||||
err := handler.FileService.RemoveDirectory(projectPath)
|
||||
if err != nil {
|
||||
if err := handler.FileService.RemoveDirectory(projectPath); err != nil {
|
||||
log.Debug().Err(err).Msg("HTTP error: unable to cleanup stack creation")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package webhooks
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -26,15 +25,12 @@ import (
|
|||
// @failure 500
|
||||
// @router /webhooks/{id} [post]
|
||||
func (handler *Handler) webhookExecute(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
|
||||
webhookToken, err := request.RetrieveRouteVariableValue(r, "token")
|
||||
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Invalid service id parameter", err)
|
||||
}
|
||||
|
||||
webhook, err := handler.DataStore.Webhook().WebhookByToken(webhookToken)
|
||||
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a webhook with this token", err)
|
||||
} else if err != nil {
|
||||
|
@ -118,15 +114,12 @@ func (handler *Handler) executeServiceWebhook(
|
|||
if err != nil {
|
||||
return httperror.NotFound("Error pulling image with the specified tag", err)
|
||||
}
|
||||
defer func(rc io.ReadCloser) {
|
||||
_ = rc.Close()
|
||||
}(rc)
|
||||
defer rc.Close()
|
||||
}
|
||||
|
||||
_, err = dockerClient.ServiceUpdate(context.Background(), resourceID, service.Version, service.Spec, serviceUpdateOptions)
|
||||
|
||||
if err != nil {
|
||||
if _, err := dockerClient.ServiceUpdate(context.Background(), resourceID, service.Version, service.Spec, serviceUpdateOptions); err != nil {
|
||||
return httperror.InternalServerError("Error updating service", err)
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ func Test_waitingMiddleware_executesImmediately_whenNotLocked(t *testing.T) {
|
|||
|
||||
timeout := 2 * time.Second
|
||||
start := time.Now()
|
||||
|
||||
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
elapsed := time.Since(start)
|
||||
if elapsed >= timeout {
|
||||
|
@ -81,7 +82,7 @@ func Test_waitingMiddleware_executesImmediately_whenNotLocked(t *testing.T) {
|
|||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
if string(body) != "success" {
|
||||
t.Error("Didn't receive expected result from the hanlder")
|
||||
t.Error("Didn't receive expected result from the handler")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,6 +106,7 @@ func Test_waitingMiddleware_waitsForTheLockToBeReleased(t *testing.T) {
|
|||
|
||||
timeout := 10 * time.Second
|
||||
start := time.Now()
|
||||
|
||||
o.WaitingMiddleware(timeout, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
elapsed := time.Since(start)
|
||||
if elapsed >= timeout {
|
||||
|
@ -115,7 +117,7 @@ func Test_waitingMiddleware_waitsForTheLockToBeReleased(t *testing.T) {
|
|||
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
if string(body) != "success" {
|
||||
t.Error("Didn't receive expected result from the hanlder")
|
||||
t.Error("Didn't receive expected result from the handler")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ func (proxy *ProxyServer) start() error {
|
|||
err := proxy.server.Serve(listener)
|
||||
log.Debug().Str("host", proxyHost).Msg("exiting proxy server")
|
||||
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Debug().Str("host", proxyHost).Err(err).Msg("proxy server exited with an error")
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -22,7 +22,7 @@ func NewTransport(signatureService portainer.DigitalSignatureService, httpTransp
|
|||
return transport
|
||||
}
|
||||
|
||||
// RoundTrip is the implementation of the the http.RoundTripper interface
|
||||
// RoundTrip is the implementation of the http.RoundTripper interface
|
||||
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
signature, err := transport.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||
if err != nil {
|
||||
|
|
|
@ -26,17 +26,16 @@ func (transport *Transport) proxyContainerGroupRequest(request *http.Request) (*
|
|||
}
|
||||
|
||||
func (transport *Transport) proxyContainerGroupPutRequest(request *http.Request) (*http.Response, error) {
|
||||
|
||||
tokenData, err := security.RetrieveTokenData(request)
|
||||
if err != nil {
|
||||
return nil, httperror.Forbidden("Permission denied to access environment", err)
|
||||
}
|
||||
|
||||
//add a lock before processing existence check
|
||||
// Add a lock before processing existence check
|
||||
transport.mutex.Lock()
|
||||
defer transport.mutex.Unlock()
|
||||
|
||||
//generate a temp http GET request based on the current PUT request
|
||||
// Generate a temp http GET request based on the current PUT request
|
||||
validationRequest := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: request.URL,
|
||||
|
@ -45,9 +44,9 @@ func (transport *Transport) proxyContainerGroupPutRequest(request *http.Request)
|
|||
},
|
||||
}
|
||||
|
||||
//fire the request to Azure API to validate if there is an existing container instance with the same name
|
||||
//positive - reject the request
|
||||
//negative - continue the process
|
||||
// Fire the request to Azure API to validate if there is an existing container instance with the same name
|
||||
// positive - reject the request
|
||||
// negative - continue the process
|
||||
validationResponse, err := http.DefaultTransport.RoundTrip(validationRequest)
|
||||
if err != nil {
|
||||
return validationResponse, err
|
||||
|
@ -63,6 +62,7 @@ func (transport *Transport) proxyContainerGroupPutRequest(request *http.Request)
|
|||
"message": "A container instance with the same name already exists inside the selected resource group",
|
||||
}
|
||||
err = utils.RewriteResponse(resp, errObj, http.StatusConflict)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func NewTransport(credentials *portainer.AzureCredentials, dataStore dataservice
|
|||
}
|
||||
}
|
||||
|
||||
// RoundTrip is the implementation of the the http.RoundTripper interface
|
||||
// RoundTrip is the implementation of the http.RoundTripper interface
|
||||
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
return transport.proxyAzureRequest(request)
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ func NewTransport(parameters *TransportParameters, httpTransport *http.Transport
|
|||
return transport, nil
|
||||
}
|
||||
|
||||
// RoundTrip is the implementation of the the http.RoundTripper interface
|
||||
// RoundTrip is the implementation of the http.RoundTripper interface
|
||||
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
return transport.ProxyDockerRequest(request)
|
||||
}
|
||||
|
@ -489,63 +489,65 @@ func (transport *Transport) decorateRegistryAuthenticationHeader(request *http.R
|
|||
}
|
||||
|
||||
func (transport *Transport) restrictedResourceOperation(request *http.Request, resourceID string, dockerResourceID string, resourceType portainer.ResourceControlType, volumeBrowseRestrictionCheck bool) (*http.Response, error) {
|
||||
var err error
|
||||
tokenData, err := security.RetrieveTokenData(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tokenData.Role != portainer.AdministratorRole {
|
||||
if volumeBrowseRestrictionCheck {
|
||||
securitySettings, err := transport.fetchEndpointSecuritySettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenData.Role == portainer.AdministratorRole {
|
||||
return transport.executeDockerRequest(request)
|
||||
}
|
||||
|
||||
if !securitySettings.AllowVolumeBrowserForRegularUsers {
|
||||
return utils.WriteAccessDeniedResponse()
|
||||
}
|
||||
}
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if volumeBrowseRestrictionCheck {
|
||||
securitySettings, err := transport.fetchEndpointSecuritySettings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userTeamIDs := make([]portainer.TeamID, 0)
|
||||
for _, membership := range teamMemberships {
|
||||
userTeamIDs = append(userTeamIDs, membership.TeamID)
|
||||
}
|
||||
|
||||
resourceControls, err := transport.dataStore.ResourceControl().ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceControl := authorization.GetResourceControlByResourceIDAndType(resourceID, resourceType, resourceControls)
|
||||
if resourceControl == nil {
|
||||
agentTargetHeader := request.Header.Get(portainer.PortainerAgentTargetHeader)
|
||||
|
||||
if dockerResourceID == "" {
|
||||
dockerResourceID = resourceID
|
||||
}
|
||||
|
||||
// This resource was created outside of portainer,
|
||||
// is part of a Docker service or part of a Docker Swarm/Compose stack.
|
||||
inheritedResourceControl, err := transport.getInheritedResourceControlFromServiceOrStack(dockerResourceID, agentTargetHeader, resourceType, resourceControls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if inheritedResourceControl == nil || !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, inheritedResourceControl) {
|
||||
return utils.WriteAccessDeniedResponse()
|
||||
}
|
||||
}
|
||||
|
||||
if resourceControl != nil && !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, resourceControl) {
|
||||
if !securitySettings.AllowVolumeBrowserForRegularUsers {
|
||||
return utils.WriteAccessDeniedResponse()
|
||||
}
|
||||
}
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userTeamIDs := make([]portainer.TeamID, 0)
|
||||
for _, membership := range teamMemberships {
|
||||
userTeamIDs = append(userTeamIDs, membership.TeamID)
|
||||
}
|
||||
|
||||
resourceControls, err := transport.dataStore.ResourceControl().ReadAll()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceControl := authorization.GetResourceControlByResourceIDAndType(resourceID, resourceType, resourceControls)
|
||||
if resourceControl == nil {
|
||||
agentTargetHeader := request.Header.Get(portainer.PortainerAgentTargetHeader)
|
||||
|
||||
if dockerResourceID == "" {
|
||||
dockerResourceID = resourceID
|
||||
}
|
||||
|
||||
// This resource was created outside of portainer,
|
||||
// is part of a Docker service or part of a Docker Swarm/Compose stack.
|
||||
inheritedResourceControl, err := transport.getInheritedResourceControlFromServiceOrStack(dockerResourceID, agentTargetHeader, resourceType, resourceControls)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if inheritedResourceControl == nil || !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, inheritedResourceControl) {
|
||||
return utils.WriteAccessDeniedResponse()
|
||||
}
|
||||
}
|
||||
|
||||
if resourceControl != nil && !authorization.UserCanAccessResource(tokenData.ID, userTeamIDs, resourceControl) {
|
||||
return utils.WriteAccessDeniedResponse()
|
||||
}
|
||||
|
||||
return transport.executeDockerRequest(request)
|
||||
}
|
||||
|
||||
|
@ -587,8 +589,7 @@ func (transport *Transport) rewriteOperation(request *http.Request, operation re
|
|||
}
|
||||
|
||||
func (transport *Transport) interceptAndRewriteRequest(request *http.Request, operation operationRequest) (*http.Response, error) {
|
||||
err := operation(request)
|
||||
if err != nil {
|
||||
if err := operation(request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -653,21 +654,22 @@ func (transport *Transport) executeGenericResourceDeletionOperation(request *htt
|
|||
return response, err
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusNoContent || response.StatusCode == http.StatusOK {
|
||||
resourceControl, err := transport.dataStore.ResourceControl().ResourceControlByResourceIDAndType(resourceIdentifierAttribute, resourceType)
|
||||
if err != nil {
|
||||
if dataservices.IsErrObjectNotFound(err) {
|
||||
return response, nil
|
||||
}
|
||||
if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
return response, err
|
||||
resourceControl, err := transport.dataStore.ResourceControl().ResourceControlByResourceIDAndType(resourceIdentifierAttribute, resourceType)
|
||||
if err != nil {
|
||||
if dataservices.IsErrObjectNotFound(err) {
|
||||
return response, nil
|
||||
}
|
||||
|
||||
if resourceControl != nil {
|
||||
err = transport.dataStore.ResourceControl().Delete(resourceControl.ID)
|
||||
if err != nil {
|
||||
return response, err
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
if resourceControl != nil {
|
||||
if err := transport.dataStore.ResourceControl().Delete(resourceControl.ID); err != nil {
|
||||
return response, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,6 +685,7 @@ func (transport *Transport) executeRequestAndRewriteResponse(request *http.Reque
|
|||
if response.StatusCode == http.StatusOK {
|
||||
err = operation(response, executor)
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
|
@ -724,17 +727,19 @@ func (transport *Transport) createRegistryAccessContext(request *http.Request) (
|
|||
}
|
||||
accessContext.registries = registries
|
||||
|
||||
if user.Role != portainer.AdministratorRole {
|
||||
accessContext.isAdmin = false
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessContext.teamMemberships = teamMemberships
|
||||
if user.Role == portainer.AdministratorRole {
|
||||
return accessContext, nil
|
||||
}
|
||||
|
||||
accessContext.isAdmin = false
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessContext.teamMemberships = teamMemberships
|
||||
|
||||
return accessContext, nil
|
||||
}
|
||||
|
||||
|
@ -756,22 +761,24 @@ func (transport *Transport) createOperationContext(request *http.Request) (*rest
|
|||
resourceControls: resourceControls,
|
||||
}
|
||||
|
||||
if tokenData.Role != portainer.AdministratorRole {
|
||||
operationContext.isAdmin = false
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userTeamIDs := make([]portainer.TeamID, 0)
|
||||
for _, membership := range teamMemberships {
|
||||
userTeamIDs = append(userTeamIDs, membership.TeamID)
|
||||
}
|
||||
|
||||
operationContext.userTeamIDs = userTeamIDs
|
||||
if tokenData.Role == portainer.AdministratorRole {
|
||||
return operationContext, nil
|
||||
}
|
||||
|
||||
operationContext.isAdmin = false
|
||||
|
||||
teamMemberships, err := transport.dataStore.TeamMembership().TeamMembershipsByUserID(tokenData.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userTeamIDs := make([]portainer.TeamID, 0)
|
||||
for _, membership := range teamMemberships {
|
||||
userTeamIDs = append(userTeamIDs, membership.TeamID)
|
||||
}
|
||||
|
||||
operationContext.userTeamIDs = userTeamIDs
|
||||
|
||||
return operationContext, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ func NewTransport() *Transport {
|
|||
}
|
||||
}
|
||||
|
||||
// RoundTrip is the implementation of the the http.RoundTripper interface
|
||||
// RoundTrip is the implementation of the http.RoundTripper interface
|
||||
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
token := request.Header.Get("Private-Token")
|
||||
if token == "" {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package factory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
|
@ -81,7 +80,7 @@ func (factory *ProxyFactory) newKubernetesEdgeHTTPProxy(endpoint *portainer.Endp
|
|||
}
|
||||
|
||||
func (factory *ProxyFactory) newKubernetesAgentHTTPSProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
|
||||
endpointURL := fmt.Sprintf("https://%s", endpoint.URL)
|
||||
endpointURL := "https://" + endpoint.URL
|
||||
remoteURL, err := url.Parse(endpointURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
|
||||
|
||||
cmap "github.com/orcaman/concurrent-map"
|
||||
"github.com/portainer/portainer/api/kubernetes/cli"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
cmap "github.com/orcaman/concurrent-map"
|
||||
)
|
||||
|
||||
type (
|
||||
// Manager represents a service used to manage proxies to environments (endpoints) and extensions.
|
||||
Manager struct {
|
||||
proxyFactory *factory.ProxyFactory
|
||||
endpointProxies cmap.ConcurrentMap
|
||||
k8sClientFactory *cli.ClientFactory
|
||||
}
|
||||
)
|
||||
var ErrProxyFactoryNotInitialized = errors.New("proxy factory not initialized")
|
||||
|
||||
// Manager represents a service used to manage proxies to environments (endpoints) and extensions.
|
||||
type Manager struct {
|
||||
proxyFactory *factory.ProxyFactory
|
||||
endpointProxies cmap.ConcurrentMap
|
||||
k8sClientFactory *cli.ClientFactory
|
||||
}
|
||||
|
||||
// NewManager initializes a new proxy Service
|
||||
func NewManager(kubernetesClientFactory *cli.ClientFactory) *Manager {
|
||||
|
@ -36,11 +36,11 @@ func (manager *Manager) NewProxyFactory(dataStore dataservices.DataStore, signat
|
|||
manager.proxyFactory = factory.NewProxyFactory(dataStore, signatureService, tunnelService, clientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService)
|
||||
}
|
||||
|
||||
// CreateAndRegisterEndpointProxy creates a new HTTP reverse proxy based on environment(endpoint) properties and and adds it to the registered proxies.
|
||||
// CreateAndRegisterEndpointProxy creates a new HTTP reverse proxy based on environment(endpoint) properties and adds it to the registered proxies.
|
||||
// It can also be used to create a new HTTP reverse proxy and replace an already registered proxy.
|
||||
func (manager *Manager) CreateAndRegisterEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
|
||||
if manager.proxyFactory == nil {
|
||||
return nil, fmt.Errorf("proxy factory not init")
|
||||
return nil, ErrProxyFactoryNotInitialized
|
||||
}
|
||||
|
||||
proxy, err := manager.proxyFactory.NewEndpointProxy(endpoint)
|
||||
|
@ -49,6 +49,7 @@ func (manager *Manager) CreateAndRegisterEndpointProxy(endpoint *portainer.Endpo
|
|||
}
|
||||
|
||||
manager.endpointProxies.Set(fmt.Sprint(endpoint.ID), proxy)
|
||||
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
|
@ -56,8 +57,9 @@ func (manager *Manager) CreateAndRegisterEndpointProxy(endpoint *portainer.Endpo
|
|||
// It can also be used to create a new HTTP reverse proxy and replace an already registered proxy.
|
||||
func (manager *Manager) CreateAgentProxyServer(endpoint *portainer.Endpoint) (*factory.ProxyServer, error) {
|
||||
if manager.proxyFactory == nil {
|
||||
return nil, fmt.Errorf("proxy factory not init")
|
||||
return nil, ErrProxyFactoryNotInitialized
|
||||
}
|
||||
|
||||
return manager.proxyFactory.NewAgentProxy(endpoint)
|
||||
}
|
||||
|
||||
|
@ -85,7 +87,8 @@ func (manager *Manager) DeleteEndpointProxy(endpointID portainer.EndpointID) {
|
|||
// CreateGitlabProxy creates a new HTTP reverse proxy that can be used to send requests to the Gitlab API
|
||||
func (manager *Manager) CreateGitlabProxy(url string) (http.Handler, error) {
|
||||
if manager.proxyFactory == nil {
|
||||
return nil, fmt.Errorf("proxy factory not init")
|
||||
return nil, ErrProxyFactoryNotInitialized
|
||||
}
|
||||
|
||||
return manager.proxyFactory.NewGitlabProxy(url)
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ func (bouncer *RequestBouncer) mwIsTeamLeader(next http.Handler) http.Handler {
|
|||
}
|
||||
|
||||
// mwAuthenticateFirst authenticates a request an auth token.
|
||||
// A result of a first succeded token lookup would be used for the authentication.
|
||||
// A result of a first succeeded token lookup would be used for the authentication.
|
||||
func (bouncer *RequestBouncer) mwAuthenticateFirst(tokenLookups []tokenLookup, next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var token *portainer.TokenData
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue