mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
chore(code): reduce the code duplication EE-7278 (#11969)
This commit is contained in:
parent
39bdfa4512
commit
9ee092aa5e
85 changed files with 520 additions and 618 deletions
|
@ -5,10 +5,10 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
|
||||
gorillacsrf "github.com/gorilla/csrf"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
|
||||
|
@ -16,8 +16,7 @@ func WithProtect(handler http.Handler) (http.Handler, error) {
|
|||
handler = withSendCSRFToken(handler)
|
||||
|
||||
token := make([]byte, 32)
|
||||
_, err := rand.Read(token)
|
||||
if err != nil {
|
||||
if _, err := rand.Read(token); err != nil {
|
||||
return nil, fmt.Errorf("failed to generate CSRF token: %w", err)
|
||||
}
|
||||
|
||||
|
@ -32,7 +31,6 @@ func WithProtect(handler http.Handler) (http.Handler, error) {
|
|||
|
||||
func withSendCSRFToken(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
sw := negroni.NewResponseWriter(w)
|
||||
|
||||
sw.Before(func(sw negroni.ResponseWriter) {
|
||||
|
@ -44,16 +42,15 @@ func withSendCSRFToken(handler http.Handler) http.Handler {
|
|||
})
|
||||
|
||||
handler.ServeHTTP(sw, r)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func withSkipCSRF(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
skip, err := security.ShouldSkipCSRFCheck(r)
|
||||
if err != nil {
|
||||
httperror.WriteError(w, http.StatusForbidden, err.Error(), err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -56,8 +56,7 @@ func (payload *authenticatePayload) Validate(r *http.Request) error {
|
|||
// @router /auth [post]
|
||||
func (handler *Handler) authenticate(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload authenticatePayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -104,8 +103,7 @@ func isUserInitialAdmin(user *portainer.User) bool {
|
|||
}
|
||||
|
||||
func (handler *Handler) authenticateInternal(w http.ResponseWriter, user *portainer.User, password string) *httperror.HandlerError {
|
||||
err := handler.CryptoService.CompareHashAndData(user.Password, password)
|
||||
if err != nil {
|
||||
if err := handler.CryptoService.CompareHashAndData(user.Password, password); err != nil {
|
||||
return httperror.NewError(http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized)
|
||||
}
|
||||
|
||||
|
@ -115,8 +113,7 @@ func (handler *Handler) authenticateInternal(w http.ResponseWriter, user *portai
|
|||
}
|
||||
|
||||
func (handler *Handler) authenticateLDAP(w http.ResponseWriter, user *portainer.User, username, password string, ldapSettings *portainer.LDAPSettings) *httperror.HandlerError {
|
||||
err := handler.LDAPService.AuthenticateUser(username, password, ldapSettings)
|
||||
if err != nil {
|
||||
if err := handler.LDAPService.AuthenticateUser(username, password, ldapSettings); err != nil {
|
||||
if errors.Is(err, httperrors.ErrUnauthorized) {
|
||||
return httperror.NewError(http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized)
|
||||
}
|
||||
|
@ -131,14 +128,12 @@ func (handler *Handler) authenticateLDAP(w http.ResponseWriter, user *portainer.
|
|||
PortainerAuthorizations: authorization.DefaultPortainerAuthorizations(),
|
||||
}
|
||||
|
||||
err = handler.DataStore.User().Create(user)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.User().Create(user); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist user inside the database", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = handler.syncUserTeamsWithLDAPGroups(user, ldapSettings)
|
||||
if err != nil {
|
||||
if err := handler.syncUserTeamsWithLDAPGroups(user, ldapSettings); err != nil {
|
||||
log.Warn().Err(err).Msg("unable to automatically sync user teams with ldap")
|
||||
}
|
||||
|
||||
|
@ -186,7 +181,6 @@ func (handler *Handler) syncUserTeamsWithLDAPGroups(user *portainer.User, settin
|
|||
|
||||
for _, team := range teams {
|
||||
if teamExists(team.Name, userGroups) {
|
||||
|
||||
if teamMembershipExists(team.ID, userMemberships) {
|
||||
continue
|
||||
}
|
||||
|
@ -197,8 +191,7 @@ func (handler *Handler) syncUserTeamsWithLDAPGroups(user *portainer.User, settin
|
|||
Role: portainer.TeamMember,
|
||||
}
|
||||
|
||||
err := handler.DataStore.TeamMembership().Create(membership)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.TeamMembership().Create(membership); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,5 +41,6 @@ func NewHandler(bouncer security.BouncerService, rateLimiter *security.RateLimit
|
|||
rateLimiter.LimitAccess(bouncer.PublicAccess(httperror.LoggerHandler(h.authenticate)))).Methods(http.MethodPost)
|
||||
h.Handle("/auth/logout",
|
||||
bouncer.PublicAccess(httperror.LoggerHandler(h.logout))).Methods(http.MethodPost)
|
||||
|
||||
return h
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/logoutcontext"
|
||||
"github.com/portainer/portainer/api/logoutcontext"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
|
|
@ -4,14 +4,15 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/portainer/portainer/api/internal/slices"
|
||||
"github.com/portainer/portainer/api/slicesx"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
@ -70,7 +71,7 @@ func (handler *Handler) customTemplateList(w http.ResponseWriter, r *http.Reques
|
|||
customTemplates = filterByType(customTemplates, templateTypes)
|
||||
|
||||
if edge != nil {
|
||||
customTemplates = slices.Filter(customTemplates, func(customTemplate portainer.CustomTemplate) bool {
|
||||
customTemplates = slicesx.Filter(customTemplates, func(customTemplate portainer.CustomTemplate) bool {
|
||||
return customTemplate.EdgeTemplate == *edge
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
"github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/http/handler/docker/utils"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"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/internal/slices"
|
||||
"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.
|
||||
|
@ -16,7 +16,7 @@ func FilterByResourceControl[T any](tx dataservices.DataStoreTx, items []T, rcTy
|
|||
return items, nil
|
||||
}
|
||||
|
||||
userTeamIDs := slices.Map(securityContext.UserMemberships, func(membership portainer.TeamMembership) portainer.TeamID {
|
||||
userTeamIDs := slicesx.Map(securityContext.UserMemberships, func(membership portainer.TeamMembership) portainer.TeamID {
|
||||
return membership.TeamID
|
||||
})
|
||||
|
||||
|
@ -32,5 +32,6 @@ func FilterByResourceControl[T any](tx dataservices.DataStoreTx, items []T, rcTy
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
return filteredItems, nil
|
||||
}
|
||||
|
|
|
@ -36,23 +36,25 @@ func (payload *edgeGroupCreatePayload) Validate(r *http.Request) error {
|
|||
func calculateEndpointsOrTags(tx dataservices.DataStoreTx, edgeGroup *portainer.EdgeGroup, endpoints []portainer.EndpointID, tagIDs []portainer.TagID) error {
|
||||
if edgeGroup.Dynamic {
|
||||
edgeGroup.TagIDs = tagIDs
|
||||
} else {
|
||||
endpointIDs := []portainer.EndpointID{}
|
||||
|
||||
for _, endpointID := range endpoints {
|
||||
endpoint, err := tx.Endpoint().Endpoint(endpointID)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve environment from the database", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if endpointutils.IsEdgeEndpoint(endpoint) {
|
||||
endpointIDs = append(endpointIDs, endpoint.ID)
|
||||
}
|
||||
endpointIDs := []portainer.EndpointID{}
|
||||
|
||||
for _, endpointID := range endpoints {
|
||||
endpoint, err := tx.Endpoint().Endpoint(endpointID)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve environment from the database", err)
|
||||
}
|
||||
|
||||
edgeGroup.Endpoints = endpointIDs
|
||||
if endpointutils.IsEdgeEndpoint(endpoint) {
|
||||
endpointIDs = append(endpointIDs, endpoint.ID)
|
||||
}
|
||||
}
|
||||
|
||||
edgeGroup.Endpoints = endpointIDs
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -71,13 +73,13 @@ func calculateEndpointsOrTags(tx dataservices.DataStoreTx, edgeGroup *portainer.
|
|||
// @router /edge_groups [post]
|
||||
func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload edgeGroupCreatePayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
var edgeGroup *portainer.EdgeGroup
|
||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
|
||||
err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
edgeGroups, err := tx.EdgeGroup().ReadAll()
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve Edge groups from the database", err)
|
||||
|
@ -101,8 +103,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
|
|||
return err
|
||||
}
|
||||
|
||||
err = tx.EdgeGroup().Create(edgeGroup)
|
||||
if err != nil {
|
||||
if err := tx.EdgeGroup().Create(edgeGroup); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the Edge group inside the database", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/internal/unique"
|
||||
"github.com/portainer/portainer/api/slicesx"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
|
||||
|
@ -113,7 +113,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
newRelatedEndpoints := edge.EdgeGroupRelatedEndpoints(edgeGroup, endpoints, endpointGroups)
|
||||
endpointsToUpdate := unique.Unique(append(newRelatedEndpoints, oldRelatedEndpoints...))
|
||||
endpointsToUpdate := slicesx.Unique(append(newRelatedEndpoints, oldRelatedEndpoints...))
|
||||
|
||||
edgeJobs, err := tx.EdgeJob().ReadAll()
|
||||
if err != nil {
|
||||
|
|
|
@ -31,8 +31,7 @@ func setupHandler(t *testing.T) (*Handler, string) {
|
|||
}
|
||||
|
||||
user := &portainer.User{ID: 2, Username: "admin", Role: portainer.AdministratorRole}
|
||||
err = store.User().Create(user)
|
||||
if err != nil {
|
||||
if err := store.User().Create(user); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -66,8 +65,7 @@ func setupHandler(t *testing.T) (*Handler, string) {
|
|||
}
|
||||
settings.EnableEdgeComputeFeatures = true
|
||||
|
||||
err = handler.DataStore.Settings().UpdateSettings(settings)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Settings().UpdateSettings(settings); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -88,8 +86,7 @@ func createEndpointWithId(t *testing.T, store dataservices.DataStore, endpointID
|
|||
LastCheckInDate: time.Now().Unix(),
|
||||
}
|
||||
|
||||
err := store.Endpoint().Create(&endpoint)
|
||||
if err != nil {
|
||||
if err := store.Endpoint().Create(&endpoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -112,8 +109,7 @@ func createEdgeStack(t *testing.T, store dataservices.DataStore, endpointID port
|
|||
PartialMatch: false,
|
||||
}
|
||||
|
||||
err := store.EdgeGroup().Create(&edgeGroup)
|
||||
if err != nil {
|
||||
if err := store.EdgeGroup().Create(&edgeGroup); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -138,13 +134,11 @@ func createEdgeStack(t *testing.T, store dataservices.DataStore, endpointID port
|
|||
},
|
||||
}
|
||||
|
||||
err = store.EdgeStack().Create(edgeStack.ID, &edgeStack)
|
||||
if err != nil {
|
||||
if err := store.EdgeStack().Create(edgeStack.ID, &edgeStack); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = store.EndpointRelation().Create(&endpointRelation)
|
||||
if err != nil {
|
||||
if err := store.EndpointRelation().Create(&endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
)
|
||||
|
@ -24,8 +25,7 @@ func TestUpdateAndInspect(t *testing.T) {
|
|||
endpointID := portainer.EndpointID(6)
|
||||
newEndpoint := createEndpointWithId(t, handler.DataStore, endpointID)
|
||||
|
||||
err := handler.DataStore.Endpoint().Create(&newEndpoint)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Endpoint().Create(&newEndpoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,7 @@ func TestUpdateAndInspect(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
err = handler.DataStore.EndpointRelation().Create(&endpointRelation)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.EndpointRelation().Create(&endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -50,8 +49,7 @@ func TestUpdateAndInspect(t *testing.T) {
|
|||
PartialMatch: false,
|
||||
}
|
||||
|
||||
err = handler.DataStore.EdgeGroup().Create(&newEdgeGroup)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.EdgeGroup().Create(&newEdgeGroup); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -96,8 +94,7 @@ func TestUpdateAndInspect(t *testing.T) {
|
|||
}
|
||||
|
||||
updatedStack := portainer.EdgeStack{}
|
||||
err = json.NewDecoder(rec.Body).Decode(&updatedStack)
|
||||
if err != nil {
|
||||
if err := json.NewDecoder(rec.Body).Decode(&updatedStack); err != nil {
|
||||
t.Fatal("error decoding response:", err)
|
||||
}
|
||||
|
||||
|
@ -120,7 +117,6 @@ func TestUpdateWithInvalidEdgeGroups(t *testing.T) {
|
|||
endpoint := createEndpoint(t, handler.DataStore)
|
||||
edgeStack := createEdgeStack(t, handler.DataStore, endpoint.ID)
|
||||
|
||||
//newEndpoint := createEndpoint(t, handler.DataStore)
|
||||
newEdgeGroup := portainer.EdgeGroup{
|
||||
ID: 2,
|
||||
Name: "EdgeGroup 2",
|
||||
|
@ -130,7 +126,8 @@ func TestUpdateWithInvalidEdgeGroups(t *testing.T) {
|
|||
PartialMatch: false,
|
||||
}
|
||||
|
||||
handler.DataStore.EdgeGroup().Create(&newEdgeGroup)
|
||||
err := handler.DataStore.EdgeGroup().Create(&newEdgeGroup)
|
||||
require.NoError(t, err)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
|
||||
"github.com/segmentio/encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type endpointTestCase struct {
|
||||
|
@ -99,8 +100,7 @@ func mustSetupHandler(t *testing.T) *Handler {
|
|||
}
|
||||
settings.TrustOnFirstConnect = true
|
||||
|
||||
err = store.Settings().UpdateSettings(settings)
|
||||
if err != nil {
|
||||
if err = store.Settings().UpdateSettings(settings); err != nil {
|
||||
t.Fatalf("could not update settings: %s", err)
|
||||
}
|
||||
|
||||
|
@ -122,8 +122,7 @@ func createEndpoint(handler *Handler, endpoint portainer.Endpoint, endpointRelat
|
|||
return nil
|
||||
}
|
||||
|
||||
err = handler.DataStore.Endpoint().Create(&endpoint)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Endpoint().Create(&endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -134,14 +133,13 @@ func TestMissingEdgeIdentifier(t *testing.T) {
|
|||
handler := mustSetupHandler(t)
|
||||
endpointID := portainer.EndpointID(45)
|
||||
|
||||
err := createEndpoint(handler, portainer.Endpoint{
|
||||
if err := createEndpoint(handler, portainer.Endpoint{
|
||||
ID: endpointID,
|
||||
Name: "endpoint-id-45",
|
||||
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||
URL: "https://portainer.io:9443",
|
||||
EdgeID: "edge-id",
|
||||
}, portainer.EndpointRelation{EndpointID: endpointID})
|
||||
if err != nil {
|
||||
}, portainer.EndpointRelation{EndpointID: endpointID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -201,8 +199,7 @@ func TestLastCheckInDateIncreases(t *testing.T) {
|
|||
EndpointID: endpoint.ID,
|
||||
}
|
||||
|
||||
err := createEndpoint(handler, endpoint, endpointRelation)
|
||||
if err != nil {
|
||||
if err := createEndpoint(handler, endpoint, endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -212,6 +209,7 @@ func TestLastCheckInDateIncreases(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("request error:", err)
|
||||
}
|
||||
|
||||
req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
|
||||
req.Header.Set(portainer.HTTPResponseAgentPlatform, "1")
|
||||
|
||||
|
@ -246,8 +244,7 @@ func TestEmptyEdgeIdWithAgentPlatformHeader(t *testing.T) {
|
|||
EndpointID: endpoint.ID,
|
||||
}
|
||||
|
||||
err := createEndpoint(handler, endpoint, endpointRelation)
|
||||
if err != nil {
|
||||
if err := createEndpoint(handler, endpoint, endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -255,6 +252,7 @@ func TestEmptyEdgeIdWithAgentPlatformHeader(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("request error:", err)
|
||||
}
|
||||
|
||||
req.Header.Set(portainer.PortainerAgentEdgeIDHeader, edgeId)
|
||||
req.Header.Set(portainer.HTTPResponseAgentPlatform, "1")
|
||||
|
||||
|
@ -308,10 +306,11 @@ func TestEdgeStackStatus(t *testing.T) {
|
|||
edgeStack.ID: true,
|
||||
},
|
||||
}
|
||||
handler.DataStore.EdgeStack().Create(edgeStack.ID, &edgeStack)
|
||||
|
||||
err := createEndpoint(handler, endpoint, endpointRelation)
|
||||
if err != nil {
|
||||
err := handler.DataStore.EdgeStack().Create(edgeStack.ID, &edgeStack)
|
||||
require.NoError(t, err)
|
||||
|
||||
if err := createEndpoint(handler, endpoint, endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -319,6 +318,7 @@ func TestEdgeStackStatus(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("request error:", err)
|
||||
}
|
||||
|
||||
req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
|
||||
req.Header.Set(portainer.HTTPResponseAgentPlatform, "1")
|
||||
|
||||
|
@ -330,8 +330,7 @@ func TestEdgeStackStatus(t *testing.T) {
|
|||
}
|
||||
|
||||
var data endpointEdgeStatusInspectResponse
|
||||
err = json.NewDecoder(rec.Body).Decode(&data)
|
||||
if err != nil {
|
||||
if err := json.NewDecoder(rec.Body).Decode(&data); err != nil {
|
||||
t.Fatal("error decoding response:", err)
|
||||
}
|
||||
|
||||
|
@ -357,8 +356,7 @@ func TestEdgeJobsResponse(t *testing.T) {
|
|||
EndpointID: endpoint.ID,
|
||||
}
|
||||
|
||||
err := createEndpoint(handler, endpoint, endpointRelation)
|
||||
if err != nil {
|
||||
if err := createEndpoint(handler, endpoint, endpointRelation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -384,6 +382,7 @@ func TestEdgeJobsResponse(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal("request error:", err)
|
||||
}
|
||||
|
||||
req.Header.Set(portainer.PortainerAgentEdgeIDHeader, "edge-id")
|
||||
req.Header.Set(portainer.HTTPResponseAgentPlatform, "1")
|
||||
|
||||
|
@ -395,8 +394,7 @@ func TestEdgeJobsResponse(t *testing.T) {
|
|||
}
|
||||
|
||||
var data endpointEdgeStatusInspectResponse
|
||||
err = json.NewDecoder(rec.Body).Decode(&data)
|
||||
if err != nil {
|
||||
if err := json.NewDecoder(rec.Body).Decode(&data); err != nil {
|
||||
t.Fatal("error decoding response:", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ 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"
|
||||
"github.com/portainer/portainer/api/tag"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
|
|
@ -73,8 +73,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
payload.GroupID = groupID
|
||||
|
||||
var tagIDs []portainer.TagID
|
||||
err = request.RetrieveMultiPartFormJSONValue(r, "TagIds", &tagIDs, true)
|
||||
if err != nil {
|
||||
if err := request.RetrieveMultiPartFormJSONValue(r, "TagIds", &tagIDs, true); err != nil {
|
||||
return errors.New("invalid TagIds parameter")
|
||||
}
|
||||
payload.TagIDs = tagIDs
|
||||
|
@ -96,6 +95,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
if err != nil {
|
||||
return errors.New("invalid CA certificate file. Ensure that the file is uploaded correctly")
|
||||
}
|
||||
|
||||
payload.TLSCACertFile = caCert
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
if err != nil {
|
||||
return errors.New("invalid key file. Ensure that the file is uploaded correctly")
|
||||
}
|
||||
|
||||
payload.TLSKeyFile = key
|
||||
}
|
||||
}
|
||||
|
@ -120,6 +121,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
if err != nil {
|
||||
return errors.New("invalid Azure application ID")
|
||||
}
|
||||
|
||||
payload.AzureApplicationID = azureApplicationID
|
||||
|
||||
azureTenantID, err := request.RetrieveMultiPartFormValue(r, "AzureTenantID", false)
|
||||
|
@ -139,6 +141,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
if err != nil || strings.EqualFold("", strings.Trim(endpointURL, " ")) {
|
||||
return errors.New("URL cannot be empty")
|
||||
}
|
||||
|
||||
payload.URL = endpointURL
|
||||
|
||||
publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true)
|
||||
|
@ -156,10 +159,10 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
}
|
||||
|
||||
gpus := make([]portainer.Pair, 0)
|
||||
err = request.RetrieveMultiPartFormJSONValue(r, "Gpus", &gpus, true)
|
||||
if err != nil {
|
||||
if err := request.RetrieveMultiPartFormJSONValue(r, "Gpus", &gpus, true); err != nil {
|
||||
return errors.New("invalid Gpus parameter")
|
||||
}
|
||||
|
||||
payload.Gpus = gpus
|
||||
|
||||
edgeCheckinInterval, _ := request.RetrieveNumericMultiPartFormValue(r, "EdgeCheckinInterval", true)
|
||||
|
@ -206,8 +209,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
|
|||
// @router /endpoints [post]
|
||||
func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
payload := &endpointCreatePayload{}
|
||||
err := payload.Validate(r)
|
||||
if err != nil {
|
||||
if err := payload.Validate(r); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -268,8 +270,7 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
|
|||
)
|
||||
}
|
||||
|
||||
err = handler.DataStore.EndpointRelation().Create(relationObject)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.EndpointRelation().Create(relationObject); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the relation object inside the database", err)
|
||||
}
|
||||
|
||||
|
@ -278,6 +279,7 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
|
|||
|
||||
func (handler *Handler) createEndpoint(tx dataservices.DataStoreTx, payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
|
||||
var err error
|
||||
|
||||
switch payload.EndpointCreationType {
|
||||
case azureEnvironment:
|
||||
return handler.createAzureEndpoint(tx, payload)
|
||||
|
@ -329,8 +331,7 @@ func (handler *Handler) createAzureEndpoint(tx dataservices.DataStoreTx, payload
|
|||
}
|
||||
|
||||
httpClient := client.NewHTTPClient()
|
||||
_, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
|
||||
if err != nil {
|
||||
if _, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials); err != nil {
|
||||
return nil, httperror.InternalServerError("Unable to authenticate against Azure", err)
|
||||
}
|
||||
|
||||
|
@ -352,8 +353,7 @@ func (handler *Handler) createAzureEndpoint(tx dataservices.DataStoreTx, payload
|
|||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
err = handler.saveEndpointAndUpdateAuthorizations(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.saveEndpointAndUpdateAuthorizations(tx, endpoint); err != nil {
|
||||
return nil, httperror.InternalServerError("An error occurred while trying to create the environment", err)
|
||||
}
|
||||
|
||||
|
@ -405,8 +405,7 @@ func (handler *Handler) createEdgeAgentEndpoint(tx dataservices.DataStoreTx, pay
|
|||
endpoint.EdgeID = edgeID.String()
|
||||
}
|
||||
|
||||
err = handler.saveEndpointAndUpdateAuthorizations(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.saveEndpointAndUpdateAuthorizations(tx, endpoint); err != nil {
|
||||
return nil, httperror.InternalServerError("An error occurred while trying to create the environment", err)
|
||||
}
|
||||
|
||||
|
@ -443,8 +442,7 @@ func (handler *Handler) createUnsecuredEndpoint(tx dataservices.DataStoreTx, pay
|
|||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
err := handler.snapshotAndPersistEndpoint(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.snapshotAndPersistEndpoint(tx, endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -478,8 +476,7 @@ func (handler *Handler) createKubernetesEndpoint(tx dataservices.DataStoreTx, pa
|
|||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
err := handler.snapshotAndPersistEndpoint(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.snapshotAndPersistEndpoint(tx, endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -510,13 +507,11 @@ func (handler *Handler) createTLSSecuredEndpoint(tx dataservices.DataStoreTx, pa
|
|||
|
||||
endpoint.Agent.Version = agentVersion
|
||||
|
||||
err := handler.storeTLSFiles(endpoint, payload)
|
||||
if err != nil {
|
||||
if err := handler.storeTLSFiles(endpoint, payload); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = handler.snapshotAndPersistEndpoint(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.snapshotAndPersistEndpoint(tx, endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -524,17 +519,16 @@ func (handler *Handler) createTLSSecuredEndpoint(tx dataservices.DataStoreTx, pa
|
|||
}
|
||||
|
||||
func (handler *Handler) snapshotAndPersistEndpoint(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint) *httperror.HandlerError {
|
||||
err := handler.SnapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
if err := handler.SnapshotService.SnapshotEndpoint(endpoint); err != nil {
|
||||
if (endpoint.Type == portainer.AgentOnDockerEnvironment && strings.Contains(err.Error(), "Invalid request signature")) ||
|
||||
(endpoint.Type == portainer.AgentOnKubernetesEnvironment && strings.Contains(err.Error(), "unknown")) {
|
||||
err = errors.New("agent already paired with another Portainer instance")
|
||||
}
|
||||
|
||||
return httperror.InternalServerError("Unable to initiate communications with environment", err)
|
||||
}
|
||||
|
||||
err = handler.saveEndpointAndUpdateAuthorizations(tx, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.saveEndpointAndUpdateAuthorizations(tx, endpoint); err != nil {
|
||||
return httperror.InternalServerError("An error occurred while trying to create the environment", err)
|
||||
}
|
||||
|
||||
|
@ -555,16 +549,14 @@ func (handler *Handler) saveEndpointAndUpdateAuthorizations(tx dataservices.Data
|
|||
AllowStackManagementForRegularUsers: true,
|
||||
}
|
||||
|
||||
err := tx.Endpoint().Create(endpoint)
|
||||
if err != nil {
|
||||
if err := tx.Endpoint().Create(endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, tagID := range endpoint.TagIDs {
|
||||
err = tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
|
||||
if err := tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
|
||||
tag.Endpoints[endpoint.ID] = true
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -580,22 +572,26 @@ func (handler *Handler) storeTLSFiles(endpoint *portainer.Endpoint, payload *end
|
|||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist TLS CA certificate file on disk", err)
|
||||
}
|
||||
|
||||
endpoint.TLSConfig.TLSCACertPath = caCertPath
|
||||
}
|
||||
|
||||
if !payload.TLSSkipClientVerify {
|
||||
certPath, err := handler.FileService.StoreTLSFileFromBytes(folder, portainer.TLSFileCert, payload.TLSCertFile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist TLS certificate file on disk", err)
|
||||
}
|
||||
endpoint.TLSConfig.TLSCertPath = certPath
|
||||
|
||||
keyPath, err := handler.FileService.StoreTLSFileFromBytes(folder, portainer.TLSFileKey, payload.TLSKeyFile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist TLS key file on disk", err)
|
||||
}
|
||||
endpoint.TLSConfig.TLSKeyPath = keyPath
|
||||
if payload.TLSSkipClientVerify {
|
||||
return nil
|
||||
}
|
||||
|
||||
certPath, err := handler.FileService.StoreTLSFileFromBytes(folder, portainer.TLSFileCert, payload.TLSCertFile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist TLS certificate file on disk", err)
|
||||
}
|
||||
|
||||
endpoint.TLSConfig.TLSCertPath = certPath
|
||||
|
||||
keyPath, err := handler.FileService.StoreTLSFileFromBytes(folder, portainer.TLSFileKey, payload.TLSKeyFile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist TLS key file on disk", err)
|
||||
}
|
||||
endpoint.TLSConfig.TLSKeyPath = keyPath
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -30,24 +30,22 @@ func TestEndpointDeleteEdgeGroupsConcurrently(t *testing.T) {
|
|||
for i := 0; i < endpointsCount; i++ {
|
||||
endpointID := portainer.EndpointID(i) + 1
|
||||
|
||||
err := store.Endpoint().Create(&portainer.Endpoint{
|
||||
if err := store.Endpoint().Create(&portainer.Endpoint{
|
||||
ID: endpointID,
|
||||
Name: "env-" + strconv.Itoa(int(endpointID)),
|
||||
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
t.Fatal("could not create endpoint:", err)
|
||||
}
|
||||
|
||||
endpointIDs = append(endpointIDs, endpointID)
|
||||
}
|
||||
|
||||
err := store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
if err := store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
ID: 1,
|
||||
Name: "edgegroup-1",
|
||||
Endpoints: endpointIDs,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
t.Fatal("could not create edge group:", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -102,7 +102,6 @@ func Test_EndpointList_AgentVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_endpointList_edgeFilter(t *testing.T) {
|
||||
|
||||
trustedEdgeAsync := portainer.Endpoint{ID: 1, UserTrusted: true, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: true}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
untrustedEdgeAsync := portainer.Endpoint{ID: 2, UserTrusted: false, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: true}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
regularUntrustedEdgeStandard := portainer.Endpoint{ID: 3, UserTrusted: false, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: false}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
|
@ -227,8 +226,7 @@ func doEndpointListRequest(req *http.Request, h *Handler, is *assert.Assertions)
|
|||
}
|
||||
|
||||
resp := []portainer.Endpoint{}
|
||||
err = json.Unmarshal(body, &resp)
|
||||
if err != nil {
|
||||
if err := json.Unmarshal(body, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -34,12 +34,10 @@ func (handler *Handler) endpointRegistriesList(w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
|
||||
var registries []portainer.Registry
|
||||
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
|
||||
if err := handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
|
||||
registries, err = handler.listRegistries(tx, r, portainer.EndpointID(endpointID))
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
var httpErr *httperror.HandlerError
|
||||
if errors.As(err, &httpErr) {
|
||||
return httpErr
|
||||
|
@ -104,11 +102,9 @@ func (handler *Handler) filterKubernetesEndpointRegistries(r *http.Request, regi
|
|||
}
|
||||
|
||||
if namespaceParam != "" {
|
||||
authorized, err := handler.isNamespaceAuthorized(endpoint, namespaceParam, user.ID, memberships, isAdmin)
|
||||
if err != nil {
|
||||
if authorized, err := handler.isNamespaceAuthorized(endpoint, namespaceParam, user.ID, memberships, isAdmin); err != nil {
|
||||
return nil, httperror.NotFound("Unable to check for namespace authorization", err)
|
||||
}
|
||||
if !authorized {
|
||||
} else if !authorized {
|
||||
return nil, httperror.Forbidden("User is not authorized to use namespace", errors.New("user is not authorized to use namespace"))
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/portainer/portainer/api/http/handler/edgegroups"
|
||||
"github.com/portainer/portainer/api/internal/edge"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/internal/unique"
|
||||
"github.com/portainer/portainer/api/slicesx"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -254,6 +254,7 @@ func filterEndpointsByEdgeStack(endpoints []portainer.Endpoint, edgeStackId port
|
|||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Unable to retrieve edge group from the database")
|
||||
}
|
||||
|
||||
if edgeGroup.Dynamic {
|
||||
endpointIDs, err := edgegroups.GetEndpointsByTags(datastore, edgeGroup.TagIDs, edgeGroup.PartialMatch)
|
||||
if err != nil {
|
||||
|
@ -261,6 +262,7 @@ func filterEndpointsByEdgeStack(endpoints []portainer.Endpoint, edgeStackId port
|
|||
}
|
||||
edgeGroup.Endpoints = endpointIDs
|
||||
}
|
||||
|
||||
envIds = append(envIds, edgeGroup.Endpoints...)
|
||||
}
|
||||
|
||||
|
@ -275,7 +277,7 @@ func filterEndpointsByEdgeStack(endpoints []portainer.Endpoint, edgeStackId port
|
|||
envIds = envIds[:n]
|
||||
}
|
||||
|
||||
uniqueIds := unique.Unique(envIds)
|
||||
uniqueIds := slicesx.Unique(envIds)
|
||||
filteredEndpoints := filteredEndpointsByIds(endpoints, uniqueIds)
|
||||
|
||||
return filteredEndpoints, nil
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/internal/slices"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/slicesx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -129,7 +129,7 @@ func Test_Filter_edgeFilter(t *testing.T) {
|
|||
func Test_Filter_excludeIDs(t *testing.T) {
|
||||
ids := []portainer.EndpointID{1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
|
||||
environments := slices.Map(ids, func(id portainer.EndpointID) portainer.Endpoint {
|
||||
environments := slicesx.Map(ids, func(id portainer.EndpointID) portainer.Endpoint {
|
||||
return portainer.Endpoint{ID: id, GroupID: 1, Type: portainer.DockerEnvironment}
|
||||
})
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ import (
|
|||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/slices"
|
||||
"github.com/portainer/portainer/api/slicesx"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -162,7 +163,7 @@ func TestSortEndpointsByField(t *testing.T) {
|
|||
}
|
||||
|
||||
func getEndpointIDs(environments []portainer.Endpoint) []portainer.EndpointID {
|
||||
return slices.Map(environments, func(environment portainer.Endpoint) portainer.EndpointID {
|
||||
return slicesx.Map(environments, func(environment portainer.Endpoint) portainer.EndpointID {
|
||||
return environment.ID
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
)
|
||||
|
||||
// updateEdgeRelations updates the edge stacks associated to an edge endpoint
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
)
|
||||
|
||||
func updateEnvironmentEdgeGroups(tx dataservices.DataStoreTx, newEdgeGroups []portainer.EdgeGroupID, environmentID portainer.EndpointID) (bool, error) {
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
)
|
||||
|
||||
func Test_updateEdgeGroups(t *testing.T) {
|
||||
|
||||
createGroups := func(store *datastore.Store, names []string) ([]portainer.EdgeGroup, error) {
|
||||
groups := make([]portainer.EdgeGroup, len(names))
|
||||
for index, name := range names {
|
||||
|
@ -21,8 +20,7 @@ func Test_updateEdgeGroups(t *testing.T) {
|
|||
Endpoints: make([]portainer.EndpointID, 0),
|
||||
}
|
||||
|
||||
err := store.EdgeGroup().Create(group)
|
||||
if err != nil {
|
||||
if err := store.EdgeGroup().Create(group); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -42,6 +40,7 @@ func Test_updateEdgeGroups(t *testing.T) {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
is.Fail("expected endpoint to be in group")
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +51,7 @@ func Test_updateEdgeGroups(t *testing.T) {
|
|||
for j, tag := range groups {
|
||||
if tag.Name == tagName {
|
||||
result[i] = groups[j]
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ func Test_updateEdgeGroups(t *testing.T) {
|
|||
}
|
||||
|
||||
expectedGroups := groupsByName(groups, testCase.groupsToApply)
|
||||
|
||||
expectedIDs := make([]portainer.EdgeGroupID, len(expectedGroups))
|
||||
for i, tag := range expectedGroups {
|
||||
expectedIDs[i] = tag.ID
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/set"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
)
|
||||
|
||||
// updateEnvironmentTags updates the tags associated to an environment
|
||||
|
|
|
@ -10,14 +10,14 @@ import (
|
|||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_helmDelete(t *testing.T) {
|
||||
|
|
|
@ -97,13 +97,13 @@ func (handler *Handler) userHasRegistryAccess(r *http.Request) (hasAccess bool,
|
|||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint); err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) *
|
|||
}
|
||||
|
||||
publicSettings := generatePublicSettings(settings)
|
||||
|
||||
return response.JSON(w, publicSettings)
|
||||
}
|
||||
|
||||
|
@ -96,7 +97,7 @@ func generatePublicSettings(appSettings *portainer.Settings) *publicSettingsResp
|
|||
|
||||
publicSettings.IsDockerDesktopExtension = appSettings.IsDockerDesktopExtension
|
||||
|
||||
//if OAuth authentication is on, compose the related fields from application settings
|
||||
// If OAuth authentication is on, compose the related fields from application settings
|
||||
if publicSettings.AuthenticationMethod == portainer.AuthenticationOAuth {
|
||||
publicSettings.OAuthLogoutURI = appSettings.OAuthSettings.LogoutURI
|
||||
publicSettings.OAuthLoginURI = fmt.Sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s",
|
||||
|
@ -104,16 +105,18 @@ func generatePublicSettings(appSettings *portainer.Settings) *publicSettingsResp
|
|||
appSettings.OAuthSettings.ClientID,
|
||||
appSettings.OAuthSettings.RedirectURI,
|
||||
appSettings.OAuthSettings.Scopes)
|
||||
//control prompt=login param according to the SSO setting
|
||||
|
||||
// Control prompt=login param according to the SSO setting
|
||||
if !appSettings.OAuthSettings.SSO {
|
||||
publicSettings.OAuthLoginURI += "&prompt=login"
|
||||
}
|
||||
}
|
||||
//if LDAP authentication is on, compose the related fields from application settings
|
||||
// If LDAP authentication is on, compose the related fields from application settings
|
||||
if publicSettings.AuthenticationMethod == portainer.AuthenticationLDAP && appSettings.LDAPSettings.GroupSearchSettings != nil {
|
||||
if len(appSettings.LDAPSettings.GroupSearchSettings) > 0 {
|
||||
publicSettings.TeamSync = len(appSettings.LDAPSettings.GroupSearchSettings[0].GroupBaseDN) > 0
|
||||
}
|
||||
}
|
||||
|
||||
return publicSettings
|
||||
}
|
||||
|
|
|
@ -40,14 +40,17 @@ func setup() {
|
|||
|
||||
func TestGeneratePublicSettingsWithSSO(t *testing.T) {
|
||||
setup()
|
||||
|
||||
mockAppSettings.OAuthSettings.SSO = true
|
||||
publicSettings := generatePublicSettings(mockAppSettings)
|
||||
if publicSettings.AuthenticationMethod != portainer.AuthenticationOAuth {
|
||||
t.Errorf("wrong AuthenticationMethod, want: %d, got: %d", portainer.AuthenticationOAuth, publicSettings.AuthenticationMethod)
|
||||
}
|
||||
|
||||
if publicSettings.OAuthLoginURI != dummyOAuthLoginURI {
|
||||
t.Errorf("wrong OAuthLoginURI when SSO is switched on, want: %s, got: %s", dummyOAuthLoginURI, publicSettings.OAuthLoginURI)
|
||||
}
|
||||
|
||||
if publicSettings.OAuthLogoutURI != dummyOAuthLogoutURI {
|
||||
t.Errorf("wrong OAuthLogoutURI, want: %s, got: %s", dummyOAuthLogoutURI, publicSettings.OAuthLogoutURI)
|
||||
}
|
||||
|
@ -55,15 +58,18 @@ func TestGeneratePublicSettingsWithSSO(t *testing.T) {
|
|||
|
||||
func TestGeneratePublicSettingsWithoutSSO(t *testing.T) {
|
||||
setup()
|
||||
|
||||
mockAppSettings.OAuthSettings.SSO = false
|
||||
publicSettings := generatePublicSettings(mockAppSettings)
|
||||
if publicSettings.AuthenticationMethod != portainer.AuthenticationOAuth {
|
||||
t.Errorf("wrong AuthenticationMethod, want: %d, got: %d", portainer.AuthenticationOAuth, publicSettings.AuthenticationMethod)
|
||||
}
|
||||
|
||||
expectedOAuthLoginURI := dummyOAuthLoginURI + "&prompt=login"
|
||||
if publicSettings.OAuthLoginURI != expectedOAuthLoginURI {
|
||||
t.Errorf("wrong OAuthLoginURI when SSO is switched off, want: %s, got: %s", expectedOAuthLoginURI, publicSettings.OAuthLoginURI)
|
||||
}
|
||||
|
||||
if publicSettings.OAuthLogoutURI != dummyOAuthLogoutURI {
|
||||
t.Errorf("wrong OAuthLogoutURI, want: %s, got: %s", dummyOAuthLogoutURI, publicSettings.OAuthLogoutURI)
|
||||
}
|
||||
|
|
|
@ -89,8 +89,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt
|
|||
}
|
||||
|
||||
if !isOrphaned {
|
||||
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 endpoint", err)
|
||||
}
|
||||
|
||||
|
@ -119,25 +118,21 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt
|
|||
deployments.StopAutoupdate(stack.ID, stack.AutoUpdate.JobID, handler.Scheduler)
|
||||
}
|
||||
|
||||
err = handler.deleteStack(securityContext.UserID, stack, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.deleteStack(securityContext.UserID, stack, endpoint); err != nil {
|
||||
return httperror.InternalServerError(err.Error(), err)
|
||||
}
|
||||
|
||||
err = handler.DataStore.Stack().Delete(portainer.StackID(id))
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Stack().Delete(portainer.StackID(id)); err != nil {
|
||||
return httperror.InternalServerError("Unable to remove the stack from the database", err)
|
||||
}
|
||||
|
||||
if resourceControl != nil {
|
||||
err = handler.DataStore.ResourceControl().Delete(resourceControl.ID)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.ResourceControl().Delete(resourceControl.ID); err != nil {
|
||||
return httperror.InternalServerError("Unable to remove the associated resource control from the database", err)
|
||||
}
|
||||
}
|
||||
|
||||
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 remove stack files from disk")
|
||||
}
|
||||
|
||||
|
@ -169,8 +164,7 @@ func (handler *Handler) deleteExternalStack(r *http.Request, w http.ResponseWrit
|
|||
return httperror.InternalServerError("Unable to find the endpoint 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 endpoint", err)
|
||||
}
|
||||
|
||||
|
@ -179,8 +173,7 @@ func (handler *Handler) deleteExternalStack(r *http.Request, w http.ResponseWrit
|
|||
Type: portainer.DockerSwarmStack,
|
||||
}
|
||||
|
||||
err = handler.deleteStack(securityContext.UserID, stack, endpoint)
|
||||
if err != nil {
|
||||
if err := handler.deleteStack(securityContext.UserID, stack, endpoint); err != nil {
|
||||
return httperror.InternalServerError("Unable to delete stack", err)
|
||||
}
|
||||
|
||||
|
@ -255,6 +248,7 @@ func (handler *Handler) deleteStack(userID portainer.UserID, stack *portainer.St
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.WithMessagef(err, "failed to remove kubernetes resources: %q", out)
|
||||
}
|
||||
|
||||
|
@ -369,18 +363,18 @@ func (handler *Handler) stackDeleteKubernetesByName(w http.ResponseWriter, r *ht
|
|||
if err != nil {
|
||||
log.Err(err).Msgf("Unable to delete Kubernetes stack `%d`", stack.ID)
|
||||
errors = append(errors, err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err = handler.DataStore.Stack().Delete(stack.ID)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.Stack().Delete(stack.ID); err != nil {
|
||||
errors = append(errors, err)
|
||||
log.Err(err).Msgf("Unable to remove the stack `%d` from the database", stack.ID)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err = handler.FileService.RemoveDirectory(stack.ProjectPath)
|
||||
if err != nil {
|
||||
if err := handler.FileService.RemoveDirectory(stack.ProjectPath); err != nil {
|
||||
errors = append(errors, err)
|
||||
log.Warn().Err(err).Msg("Unable to remove stack files from disk")
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ func TestTagDeleteEdgeGroupsConcurrently(t *testing.T) {
|
|||
_, store := datastore.MustNewTestStore(t, true, false)
|
||||
|
||||
user := &portainer.User{ID: 2, Username: "admin", Role: portainer.AdministratorRole}
|
||||
err := store.User().Create(user)
|
||||
if err != nil {
|
||||
if err := store.User().Create(user); err != nil {
|
||||
t.Fatal("could not create admin user:", err)
|
||||
}
|
||||
|
||||
|
@ -33,29 +32,28 @@ func TestTagDeleteEdgeGroupsConcurrently(t *testing.T) {
|
|||
for i := 0; i < tagsCount; i++ {
|
||||
tagID := portainer.TagID(i) + 1
|
||||
|
||||
err = store.Tag().Create(&portainer.Tag{
|
||||
if err := store.Tag().Create(&portainer.Tag{
|
||||
ID: tagID,
|
||||
Name: "tag-" + strconv.Itoa(int(tagID)),
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
t.Fatal("could not create tag:", err)
|
||||
}
|
||||
|
||||
tagIDs = append(tagIDs, tagID)
|
||||
}
|
||||
|
||||
err = store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
if err := store.EdgeGroup().Create(&portainer.EdgeGroup{
|
||||
ID: 1,
|
||||
Name: "edgegroup-1",
|
||||
TagIDs: tagIDs,
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
t.Fatal("could not create edge group:", err)
|
||||
}
|
||||
|
||||
// Remove the tags concurrently
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(len(tagIDs))
|
||||
|
||||
for _, tagID := range tagIDs {
|
||||
|
|
|
@ -27,6 +27,7 @@ func (payload *userCreatePayload) Validate(r *http.Request) error {
|
|||
if payload.Role != 1 && payload.Role != 2 {
|
||||
return errors.New("Invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -49,8 +50,7 @@ func (payload *userCreatePayload) Validate(r *http.Request) error {
|
|||
// @router /users [post]
|
||||
func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload userCreatePayload
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -89,11 +89,11 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http
|
|||
}
|
||||
}
|
||||
|
||||
err = handler.DataStore.User().Create(user)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.User().Create(user); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist user inside the database", err)
|
||||
}
|
||||
|
||||
hideFields(user)
|
||||
|
||||
return response.JSON(w, user)
|
||||
}
|
||||
|
|
|
@ -26,12 +26,12 @@ func Test_userList(t *testing.T) {
|
|||
|
||||
_, store := datastore.MustNewTestStore(t, true, true)
|
||||
|
||||
// create admin and standard user(s)
|
||||
// Create admin and standard user(s)
|
||||
adminUser := &portainer.User{ID: 1, Username: "admin", Role: portainer.AdministratorRole}
|
||||
err := store.User().Create(adminUser)
|
||||
is.NoError(err, "error creating admin user")
|
||||
|
||||
// setup services
|
||||
// Setup services
|
||||
jwtService, err := jwt.NewService("1h", store)
|
||||
is.NoError(err, "Error initiating jwt service")
|
||||
apiKeyService := apikey.NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
|
@ -42,7 +42,7 @@ func Test_userList(t *testing.T) {
|
|||
h := NewHandler(requestBouncer, rateLimiter, apiKeyService, passwordChecker)
|
||||
h.DataStore = store
|
||||
|
||||
// generate admin user tokens
|
||||
// Generate admin user tokens
|
||||
adminJWT, _, _ := jwtService.GenerateToken(&portainer.TokenData{ID: adminUser.ID, Username: adminUser.Username, Role: adminUser.Role})
|
||||
|
||||
// Case 1: the user is given the endpoint access directly
|
||||
|
@ -54,12 +54,12 @@ func Test_userList(t *testing.T) {
|
|||
err = store.User().Create(userWithoutEndpointAccess)
|
||||
is.NoError(err, "error creating user")
|
||||
|
||||
// create environment group
|
||||
// Create environment group
|
||||
endpointGroup := &portainer.EndpointGroup{ID: 1, Name: "default-endpoint-group"}
|
||||
err = store.EndpointGroup().Create(endpointGroup)
|
||||
is.NoError(err, "error creating endpoint group")
|
||||
|
||||
// create endpoint and user access policies
|
||||
// Create endpoint and user access policies
|
||||
userAccessPolicies := make(portainer.UserAccessPolicies, 0)
|
||||
userAccessPolicies[userWithEndpointAccess.ID] = portainer.AccessPolicy{RoleID: portainer.RoleID(userWithEndpointAccess.Role)}
|
||||
|
||||
|
@ -129,7 +129,7 @@ func Test_userList(t *testing.T) {
|
|||
err = store.User().Create(userUnderGroup)
|
||||
is.NoError(err, "error creating user")
|
||||
|
||||
// create environment group including a user
|
||||
// Create environment group including a user
|
||||
userAccessPoliciesUnderGroup := make(portainer.UserAccessPolicies, 0)
|
||||
userAccessPoliciesUnderGroup[userUnderGroup.ID] = portainer.AccessPolicy{RoleID: portainer.RoleID(userUnderGroup.Role)}
|
||||
|
||||
|
@ -137,7 +137,7 @@ func Test_userList(t *testing.T) {
|
|||
err = store.EndpointGroup().Create(endpointGroupWithUser)
|
||||
is.NoError(err, "error creating endpoint group")
|
||||
|
||||
// create endpoint
|
||||
// Create endpoint
|
||||
endpointUnderGroupWithUser := &portainer.Endpoint{ID: 2, GroupID: endpointGroupWithUser.ID}
|
||||
err = store.Endpoint().Create(endpointUnderGroupWithUser)
|
||||
is.NoError(err, "error creating endpoint")
|
||||
|
@ -182,7 +182,7 @@ func Test_userList(t *testing.T) {
|
|||
err = store.TeamMembership().Create(teamMembership)
|
||||
is.NoError(err, "error creating team membership")
|
||||
|
||||
// create environment group including a team
|
||||
// Create environment group including a team
|
||||
teamAccessPoliciesUnderGroup := make(portainer.TeamAccessPolicies, 0)
|
||||
teamAccessPoliciesUnderGroup[teamUnderGroup.ID] = portainer.AccessPolicy{RoleID: portainer.RoleID(userUnderTeam.Role)}
|
||||
|
||||
|
@ -190,7 +190,7 @@ func Test_userList(t *testing.T) {
|
|||
err = store.EndpointGroup().Create(endpointGroupWithTeam)
|
||||
is.NoError(err, "error creating endpoint group")
|
||||
|
||||
// create endpoint
|
||||
// Create endpoint
|
||||
endpointUnderGroupWithTeam := &portainer.Endpoint{ID: 3, GroupID: endpointGroupWithTeam.ID}
|
||||
err = store.Endpoint().Create(endpointUnderGroupWithTeam)
|
||||
is.NoError(err, "error creating endpoint")
|
||||
|
@ -233,12 +233,12 @@ func Test_userList(t *testing.T) {
|
|||
err = store.TeamMembership().Create(teamMembershipWithEndpointAccess)
|
||||
is.NoError(err, "error creating team membership")
|
||||
|
||||
// create environment group
|
||||
// Create environment group
|
||||
endpointGroupWithoutTeam := &portainer.EndpointGroup{ID: 4, Name: "endpoint-group-without-team"}
|
||||
err = store.EndpointGroup().Create(endpointGroupWithoutTeam)
|
||||
is.NoError(err, "error creating endpoint group")
|
||||
|
||||
// create endpoint and team access policies
|
||||
// Create endpoint and team access policies
|
||||
teamAccessPolicies := make(portainer.TeamAccessPolicies, 0)
|
||||
teamAccessPolicies[teamWithEndpointAccess.ID] = portainer.AccessPolicy{RoleID: portainer.RoleID(userUnderTeamWithEndpointAccess.Role)}
|
||||
|
||||
|
|
|
@ -19,12 +19,12 @@ func Test_updateUserRemovesAccessTokens(t *testing.T) {
|
|||
|
||||
_, store := datastore.MustNewTestStore(t, true, true)
|
||||
|
||||
// create standard user
|
||||
// Create standard user
|
||||
user := &portainer.User{ID: 2, Username: "standard", Role: portainer.StandardUserRole}
|
||||
err := store.User().Create(user)
|
||||
is.NoError(err, "error creating user")
|
||||
|
||||
// setup services
|
||||
// Setup services
|
||||
jwtService, err := jwt.NewService("1h", store)
|
||||
is.NoError(err, "Error initiating jwt service")
|
||||
apiKeyService := apikey.NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
|
|
|
@ -8,12 +8,12 @@ import (
|
|||
"net/url"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/logoutcontext"
|
||||
"github.com/portainer/portainer/api/logoutcontext"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/koding/websocketproxy"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory/agent"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
"github.com/portainer/portainer/api/internal/url"
|
||||
"github.com/portainer/portainer/api/url"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
|
@ -54,6 +54,7 @@ func decorateObject(object map[string]interface{}, resourceControl *portainer.Re
|
|||
|
||||
portainerMetadata := object["Portainer"].(map[string]interface{})
|
||||
portainerMetadata["ResourceControl"] = resourceControl
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
|
@ -64,8 +65,7 @@ func (transport *Transport) createPrivateResourceControl(
|
|||
|
||||
resourceControl := authorization.NewPrivateResourceControl(resourceIdentifier, resourceType, userID)
|
||||
|
||||
err := transport.dataStore.ResourceControl().Create(resourceControl)
|
||||
if err != nil {
|
||||
if err := transport.dataStore.ResourceControl().Create(resourceControl); err != nil {
|
||||
log.Error().
|
||||
Str("resource", resourceIdentifier).
|
||||
Err(err).
|
||||
|
@ -84,6 +84,7 @@ func (transport *Transport) userCanDeleteContainerGroup(request *http.Request, c
|
|||
|
||||
resourceIdentifier := request.URL.Path
|
||||
resourceControl := transport.findResourceControl(resourceIdentifier, context)
|
||||
|
||||
return authorization.UserCanAccessResource(context.userID, context.userTeamIDs, resourceControl)
|
||||
}
|
||||
|
||||
|
@ -136,20 +137,19 @@ func (transport *Transport) filterContainerGroups(containerGroups []interface{},
|
|||
|
||||
func (transport *Transport) removeResourceControl(containerGroup map[string]interface{}, context *azureRequestContext) error {
|
||||
containerGroupID, ok := containerGroup["id"].(string)
|
||||
if ok {
|
||||
resourceControl := transport.findResourceControl(containerGroupID, context)
|
||||
if resourceControl != nil {
|
||||
err := transport.dataStore.ResourceControl().Delete(resourceControl.ID)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if !ok {
|
||||
log.Debug().Msg("missing ID in container group")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if resourceControl := transport.findResourceControl(containerGroupID, context); resourceControl != nil {
|
||||
return transport.dataStore.ResourceControl().Delete(resourceControl.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (transport *Transport) findResourceControl(containerGroupId string, context *azureRequestContext) *portainer.ResourceControl {
|
||||
resourceControl := authorization.GetResourceControlByResourceIDAndType(containerGroupId, portainer.ContainerGroupResourceControl, context.resourceControls)
|
||||
return resourceControl
|
||||
return authorization.GetResourceControlByResourceIDAndType(containerGroupId, portainer.ContainerGroupResourceControl, context.resourceControls)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory/docker"
|
||||
"github.com/portainer/portainer/api/internal/url"
|
||||
"github.com/portainer/portainer/api/url"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
|
|
|
@ -105,8 +105,7 @@ func (transport *Transport) newResourceControlFromPortainerLabels(labelsObject m
|
|||
|
||||
resourceControl := authorization.NewRestrictedResourceControl(resourceID, resourceType, userIDs, teamIDs)
|
||||
|
||||
err := transport.dataStore.ResourceControl().Create(resourceControl)
|
||||
if err != nil {
|
||||
if err := transport.dataStore.ResourceControl().Create(resourceControl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -119,8 +118,7 @@ func (transport *Transport) newResourceControlFromPortainerLabels(labelsObject m
|
|||
func (transport *Transport) createPrivateResourceControl(resourceIdentifier string, resourceType portainer.ResourceControlType, userID portainer.UserID) (*portainer.ResourceControl, error) {
|
||||
resourceControl := authorization.NewPrivateResourceControl(resourceIdentifier, resourceType, userID)
|
||||
|
||||
err := transport.dataStore.ResourceControl().Create(resourceControl)
|
||||
if err != nil {
|
||||
if err := transport.dataStore.ResourceControl().Create(resourceControl); err != nil {
|
||||
log.Error().
|
||||
Str("resource", resourceIdentifier).
|
||||
Err(err).
|
||||
|
@ -170,6 +168,7 @@ func (transport *Transport) applyAccessControlOnResource(parameters *resourceOpe
|
|||
systemResourceControl := findSystemNetworkResourceControl(responseObject)
|
||||
if systemResourceControl != nil {
|
||||
responseObject = decorateObject(responseObject, systemResourceControl)
|
||||
|
||||
return utils.RewriteResponse(response, responseObject, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +187,7 @@ func (transport *Transport) applyAccessControlOnResource(parameters *resourceOpe
|
|||
|
||||
if executor.operationContext.isAdmin || (resourceControl != nil && authorization.UserCanAccessResource(executor.operationContext.userID, executor.operationContext.userTeamIDs, resourceControl)) {
|
||||
responseObject = decorateObject(responseObject, resourceControl)
|
||||
|
||||
return utils.RewriteResponse(response, responseObject, http.StatusOK)
|
||||
}
|
||||
|
||||
|
@ -221,6 +221,7 @@ func (transport *Transport) decorateResourceList(parameters *resourceOperationPa
|
|||
if systemResourceControl != nil {
|
||||
resourceObject = decorateObject(resourceObject, systemResourceControl)
|
||||
decoratedResourceData = append(decoratedResourceData, resourceObject)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -264,6 +265,7 @@ func (transport *Transport) filterResourceList(parameters *resourceOperationPara
|
|||
if systemResourceControl != nil {
|
||||
resourceObject = decorateObject(resourceObject, systemResourceControl)
|
||||
filteredResourceData = append(filteredResourceData, resourceObject)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -277,6 +279,7 @@ func (transport *Transport) filterResourceList(parameters *resourceOperationPara
|
|||
if context.isAdmin {
|
||||
filteredResourceData = append(filteredResourceData, resourceObject)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -334,11 +337,13 @@ func (transport *Transport) findResourceControl(resourceIdentifier string, resou
|
|||
func getStackResourceIDFromLabels(resourceLabelsObject map[string]string, endpointID portainer.EndpointID) string {
|
||||
if resourceLabelsObject[resourceLabelForDockerSwarmStackName] != "" {
|
||||
stackName := resourceLabelsObject[resourceLabelForDockerSwarmStackName]
|
||||
|
||||
return stackutils.ResourceControlID(endpointID, stackName)
|
||||
}
|
||||
|
||||
if resourceLabelsObject[resourceLabelForDockerComposeStackName] != "" {
|
||||
stackName := resourceLabelsObject[resourceLabelForDockerComposeStackName]
|
||||
|
||||
return stackutils.ResourceControlID(endpointID, stackName)
|
||||
}
|
||||
|
||||
|
@ -352,5 +357,6 @@ func decorateObject(object map[string]interface{}, resourceControl *portainer.Re
|
|||
|
||||
portainerMetadata := object["Portainer"].(map[string]interface{})
|
||||
portainerMetadata["ResourceControl"] = resourceControl
|
||||
|
||||
return object
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ import (
|
|||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
)
|
||||
|
||||
const (
|
||||
configObjectIdentifier = "ID"
|
||||
)
|
||||
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)
|
||||
|
@ -78,10 +76,9 @@ func (transport *Transport) configInspectOperation(response *http.Response, exec
|
|||
// 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
|
||||
if secretSpec := utils.GetJSONObject(responseObject, "Spec"); secretSpec != nil {
|
||||
return utils.GetJSONObject(secretSpec, "Labels")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ import (
|
|||
"github.com/portainer/portainer/api/http/proxy/factory/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
taskServiceObjectIdentifier = "ServiceID"
|
||||
)
|
||||
const taskServiceObjectIdentifier = "ServiceID"
|
||||
|
||||
// taskListOperation extracts the response as a JSON array, loop through the tasks array
|
||||
// and filter the containers based on resource controls before rewriting the response.
|
||||
|
@ -46,5 +44,6 @@ func selectorTaskLabels(responseObject map[string]interface{}) map[string]interf
|
|||
return utils.GetJSONObject(containerSpecObject, "Labels")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,19 +7,17 @@ import (
|
|||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory/utils"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/portainer/portainer/api/internal/snapshot"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
volumeObjectIdentifier = "ResourceID"
|
||||
)
|
||||
const volumeObjectIdentifier = "ResourceID"
|
||||
|
||||
func getInheritedResourceControlFromVolumeLabels(dockerClient *client.Client, endpointID portainer.EndpointID, volumeID string, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) {
|
||||
volume, err := dockerClient.VolumeInspect(context.Background(), volumeID)
|
||||
|
@ -57,14 +55,13 @@ func (transport *Transport) volumeListOperation(response *http.Response, executo
|
|||
Msg("snapshot is not filled into the endpoint.")
|
||||
}
|
||||
}
|
||||
|
||||
for _, volumeObject := range volumeData {
|
||||
volume := volumeObject.(map[string]interface{})
|
||||
|
||||
err = transport.decorateVolumeResponseWithResourceID(volume)
|
||||
if err != nil {
|
||||
if err := transport.decorateVolumeResponseWithResourceID(volume); err != nil {
|
||||
return fmt.Errorf("failed decorating volume response: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
resourceOperationParameters := &resourceOperationParameters{
|
||||
|
@ -77,6 +74,7 @@ func (transport *Transport) volumeListOperation(response *http.Response, executo
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Overwrite the original volume list
|
||||
responseObject["Volumes"] = volumeData
|
||||
}
|
||||
|
@ -94,8 +92,7 @@ func (transport *Transport) volumeInspectOperation(response *http.Response, exec
|
|||
return err
|
||||
}
|
||||
|
||||
err = transport.decorateVolumeResponseWithResourceID(responseObject)
|
||||
if err != nil {
|
||||
if err := transport.decorateVolumeResponseWithResourceID(responseObject); err != nil {
|
||||
return fmt.Errorf("failed decorating volume response: %w", err)
|
||||
}
|
||||
|
||||
|
@ -148,8 +145,7 @@ func (transport *Transport) decorateVolumeResourceCreationOperation(request *htt
|
|||
}
|
||||
defer cli.Close()
|
||||
|
||||
_, err = cli.VolumeInspect(context.Background(), volumeID)
|
||||
if err == nil {
|
||||
if _, err = cli.VolumeInspect(context.Background(), volumeID); err == nil {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusConflict,
|
||||
}, errors.New("a volume with the same name already exists")
|
||||
|
@ -164,6 +160,7 @@ func (transport *Transport) decorateVolumeResourceCreationOperation(request *htt
|
|||
if response.StatusCode == http.StatusCreated {
|
||||
err = transport.decorateVolumeCreationResponse(response, resourceType, tokenData.ID)
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
|
@ -195,7 +192,6 @@ func (transport *Transport) decorateVolumeCreationResponse(response *http.Respon
|
|||
}
|
||||
|
||||
func (transport *Transport) restrictedVolumeOperation(requestPath string, request *http.Request) (*http.Response, error) {
|
||||
|
||||
if request.Method == http.MethodGet {
|
||||
return transport.rewriteOperation(request, transport.volumeInspectOperation)
|
||||
}
|
||||
|
@ -210,6 +206,7 @@ func (transport *Transport) restrictedVolumeOperation(requestPath string, reques
|
|||
if request.Method == http.MethodDelete {
|
||||
return transport.executeGenericResourceDeletionOperation(request, resourceID, volumeName, portainer.VolumeResourceControl)
|
||||
}
|
||||
|
||||
return transport.restrictedResourceOperation(request, resourceID, volumeName, portainer.VolumeResourceControl, false)
|
||||
}
|
||||
|
||||
|
@ -218,6 +215,7 @@ func (transport *Transport) getVolumeResourceID(volumeName string) (string, erro
|
|||
if err != nil {
|
||||
return "", fmt.Errorf("failed fetching docker id: %w", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s_%s", volumeName, dockerID), nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue