diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go index e7311a1bd..068670b0e 100644 --- a/api/bolt/datastore.go +++ b/api/bolt/datastore.go @@ -15,6 +15,7 @@ import ( "github.com/portainer/portainer/api/bolt/endpoint" "github.com/portainer/portainer/api/bolt/endpointgroup" "github.com/portainer/portainer/api/bolt/endpointrelation" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/extension" "github.com/portainer/portainer/api/bolt/migrator" "github.com/portainer/portainer/api/bolt/registry" @@ -123,7 +124,7 @@ func (store *Store) MigrateData() error { } version, err := store.VersionService.DBVersion() - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { version = 0 } else if err != nil { return err diff --git a/api/bolt/errors/errors.go b/api/bolt/errors/errors.go new file mode 100644 index 000000000..c9a142189 --- /dev/null +++ b/api/bolt/errors/errors.go @@ -0,0 +1,7 @@ +package errors + +import "errors" + +var ( + ErrObjectNotFound = errors.New("Object not found inside the database") +) diff --git a/api/bolt/init.go b/api/bolt/init.go index df6277ed8..244f4e961 100644 --- a/api/bolt/init.go +++ b/api/bolt/init.go @@ -2,13 +2,14 @@ package bolt import ( portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/authorization" ) // Init creates the default data set. func (store *Store) Init() error { _, err := store.SettingsService.Settings() - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { defaultSettings := &portainer.Settings{ AuthenticationMethod: portainer.AuthenticationInternal, BlackListedLabels: make([]portainer.Pair, 0), @@ -42,7 +43,7 @@ func (store *Store) Init() error { } _, err = store.DockerHubService.DockerHub() - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { defaultDockerHub := &portainer.DockerHub{ Authentication: false, Username: "", diff --git a/api/bolt/internal/db.go b/api/bolt/internal/db.go index 9689fa691..101e1ff00 100644 --- a/api/bolt/internal/db.go +++ b/api/bolt/internal/db.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "github.com/boltdb/bolt" - "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // Itob returns an 8-byte big endian representation of v. @@ -36,7 +36,7 @@ func GetObject(db *bolt.DB, bucketName string, key []byte, object interface{}) e value := bucket.Get(key) if value == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } data = make([]byte, len(value)) diff --git a/api/bolt/migrator/migrate_dbversion0.go b/api/bolt/migrator/migrate_dbversion0.go index 04d1a93b5..1ed54c41d 100644 --- a/api/bolt/migrator/migrate_dbversion0.go +++ b/api/bolt/migrator/migrate_dbversion0.go @@ -3,6 +3,7 @@ package migrator import ( "github.com/boltdb/bolt" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/user" ) @@ -22,7 +23,7 @@ func (m *Migrator) updateAdminUserToDBVersion1() error { if err != nil { return err } - } else if err != nil && err != portainer.ErrObjectNotFound { + } else if err != nil && err != errors.ErrObjectNotFound { return err } return nil diff --git a/api/bolt/stack/stack.go b/api/bolt/stack/stack.go index 54d5facd1..a5145ba35 100644 --- a/api/bolt/stack/stack.go +++ b/api/bolt/stack/stack.go @@ -2,6 +2,7 @@ package stack import ( "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" "github.com/boltdb/bolt" @@ -64,7 +65,7 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) { } if stack == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } return nil diff --git a/api/bolt/team/team.go b/api/bolt/team/team.go index d77aed90e..e88e6a3f8 100644 --- a/api/bolt/team/team.go +++ b/api/bolt/team/team.go @@ -2,6 +2,7 @@ package team import ( "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" "github.com/boltdb/bolt" @@ -64,7 +65,7 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) { } if team == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } return nil diff --git a/api/bolt/user/user.go b/api/bolt/user/user.go index 63e3dfdc9..fa0c5c151 100644 --- a/api/bolt/user/user.go +++ b/api/bolt/user/user.go @@ -2,6 +2,7 @@ package user import ( "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" "github.com/boltdb/bolt" @@ -64,7 +65,7 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error) } if user == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } return nil }) diff --git a/api/bolt/version/version.go b/api/bolt/version/version.go index 18e9ab6d5..992224eea 100644 --- a/api/bolt/version/version.go +++ b/api/bolt/version/version.go @@ -4,7 +4,7 @@ import ( "strconv" "github.com/boltdb/bolt" - "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" ) @@ -40,7 +40,7 @@ func (service *Service) DBVersion() (int, error) { value := bucket.Get([]byte(versionKey)) if value == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } data = make([]byte, len(value)) diff --git a/api/bolt/webhook/webhook.go b/api/bolt/webhook/webhook.go index 38377cdb4..d18900de9 100644 --- a/api/bolt/webhook/webhook.go +++ b/api/bolt/webhook/webhook.go @@ -2,6 +2,7 @@ package webhook import ( "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" "github.com/boltdb/bolt" @@ -87,7 +88,7 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro } if webhook == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } return nil @@ -118,7 +119,7 @@ func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error) } if webhook == nil { - return portainer.ErrObjectNotFound + return errors.ErrObjectNotFound } return nil diff --git a/api/chisel/service.go b/api/chisel/service.go index 12ea9ef31..e66983222 100644 --- a/api/chisel/service.go +++ b/api/chisel/service.go @@ -7,11 +7,10 @@ import ( "time" "github.com/dchest/uniuri" - - cmap "github.com/orcaman/concurrent-map" - chserver "github.com/jpillora/chisel/server" - portainer "github.com/portainer/portainer/api" + cmap "github.com/orcaman/concurrent-map" + "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) const ( @@ -88,7 +87,7 @@ func (service *Service) retrievePrivateKeySeed() (string, error) { var serverInfo *portainer.TunnelServerInfo serverInfo, err := service.dataStore.TunnelServer().Info() - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { keySeed := uniuri.NewLen(16) serverInfo = &portainer.TunnelServerInfo{ diff --git a/api/cli/cli.go b/api/cli/cli.go index fed9b46d0..4bcec03fd 100644 --- a/api/cli/cli.go +++ b/api/cli/cli.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "time" "github.com/portainer/portainer/api" @@ -15,11 +16,11 @@ import ( // Service implements the CLIService interface type Service struct{} -const ( - errInvalidEndpointProtocol = portainer.Error("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://") - errSocketOrNamedPipeNotFound = portainer.Error("Unable to locate Unix socket or named pipe") - errInvalidSnapshotInterval = portainer.Error("Invalid snapshot interval") - errAdminPassExcludeAdminPassFile = portainer.Error("Cannot use --admin-password with --admin-password-file") +var ( + errInvalidEndpointProtocol = errors.New("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://") + errSocketOrNamedPipeNotFound = errors.New("Unable to locate Unix socket or named pipe") + errInvalidSnapshotInterval = errors.New("Invalid snapshot interval") + errAdminPassExcludeAdminPassFile = errors.New("Cannot use --admin-password with --admin-password-file") ) // ParseFlags parse the CLI flags and return a portainer.Flags struct diff --git a/api/docker/client.go b/api/docker/client.go index 6ccb5ad7c..dace4800d 100644 --- a/api/docker/client.go +++ b/api/docker/client.go @@ -1,6 +1,7 @@ package docker import ( + "errors" "fmt" "net/http" "strings" @@ -11,8 +12,9 @@ import ( "github.com/portainer/portainer/api/crypto" ) +var errUnsupportedEnvironmentType = errors.New("Environment not supported") + const ( - unsupportedEnvironmentType = portainer.Error("Environment not supported") defaultDockerRequestTimeout = 60 dockerClientVersion = "1.37" ) @@ -36,7 +38,7 @@ func NewClientFactory(signatureService portainer.DigitalSignatureService, revers // with an agent enabled endpoint to target a specific node in an agent cluster. func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string) (*client.Client, error) { if endpoint.Type == portainer.AzureEnvironment { - return nil, unsupportedEnvironmentType + return nil, errUnsupportedEnvironmentType } else if endpoint.Type == portainer.AgentOnDockerEnvironment { return createAgentClient(endpoint, factory.signatureService, nodeName) } else if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment { diff --git a/api/docker/errors.go b/api/docker/errors.go new file mode 100644 index 000000000..80611c8b4 --- /dev/null +++ b/api/docker/errors.go @@ -0,0 +1,8 @@ +package docker + +import "errors" + +// Docker errors +var ( + ErrUnableToPingEndpoint = errors.New("Unable to communicate with the endpoint") +) diff --git a/api/errors.go b/api/errors.go deleted file mode 100644 index 8e09838a1..000000000 --- a/api/errors.go +++ /dev/null @@ -1,117 +0,0 @@ -package portainer - -// General errors. -const ( - ErrUnauthorized = Error("Unauthorized") - ErrResourceAccessDenied = Error("Access denied to resource") - ErrAuthorizationRequired = Error("Authorization required for this operation") - ErrObjectNotFound = Error("Object not found inside the database") - ErrMissingSecurityContext = Error("Unable to find security details in request context") -) - -// User errors. -const ( - ErrUserAlreadyExists = Error("User already exists") - ErrInvalidUsername = Error("Invalid username. White spaces are not allowed") - ErrAdminAlreadyInitialized = Error("An administrator user already exists") - ErrAdminCannotRemoveSelf = Error("Cannot remove your own user account. Contact another administrator") - ErrCannotRemoveLastLocalAdmin = Error("Cannot remove the last local administrator account") -) - -// Team errors. -const ( - ErrTeamAlreadyExists = Error("Team already exists") -) - -// TeamMembership errors. -const ( - ErrTeamMembershipAlreadyExists = Error("Team membership already exists for this user and team") -) - -// ResourceControl errors. -const ( - ErrResourceControlAlreadyExists = Error("A resource control is already applied on this resource") - ErrInvalidResourceControlType = Error("Unsupported resource control type") -) - -// Endpoint errors. -const ( - ErrEndpointAccessDenied = Error("Access denied to endpoint") -) - -// Azure environment errors -const ( - ErrAzureInvalidCredentials = Error("Invalid Azure credentials") -) - -// Endpoint group errors. -const ( - ErrCannotRemoveDefaultGroup = Error("Cannot remove the default endpoint group") -) - -// Registry errors. -const ( - ErrRegistryAlreadyExists = Error("A registry is already defined for this URL") -) - -// Stack errors -const ( - ErrStackAlreadyExists = Error("A stack already exists with this name") - ErrComposeFileNotFoundInRepository = Error("Unable to find a Compose file in the repository") - ErrStackNotExternal = Error("Not an external stack") -) - -// Tag errors -const ( - ErrTagAlreadyExists = Error("A tag already exists with this name") -) - -// Endpoint extensions error -const ( - ErrEndpointExtensionNotSupported = Error("This extension is not supported") - ErrEndpointExtensionAlreadyAssociated = Error("This extension is already associated to the endpoint") -) - -// Crypto errors. -const ( - ErrCryptoHashFailure = Error("Unable to hash data") -) - -// JWT errors. -const ( - ErrSecretGeneration = Error("Unable to generate secret key") - ErrInvalidJWTToken = Error("Invalid JWT token") - ErrMissingContextData = Error("Unable to find JWT data in request context") -) - -// File errors. -const ( - ErrUndefinedTLSFileType = Error("Undefined TLS file type") -) - -// Extension errors. -const ( - ErrExtensionAlreadyEnabled = Error("This extension is already enabled") -) - -// Docker errors. -const ( - ErrUnableToPingEndpoint = Error("Unable to communicate with the endpoint") -) - -// Schedule errors. -const ( - ErrHostManagementFeaturesDisabled = Error("Host management features are disabled") -) - -// Error represents an application error. -type Error string - -// Error returns the error message. -func (e Error) Error() string { return string(e) } - -// Webhook errors -const ( - ErrWebhookAlreadyExists = Error("A webhook for this resource already exists") - ErrUnsupportedWebhookType = Error("Webhooks for this resource are not currently supported") -) diff --git a/api/exec/swarm_stack.go b/api/exec/swarm_stack.go index 94d9e0904..31fb48836 100644 --- a/api/exec/swarm_stack.go +++ b/api/exec/swarm_stack.go @@ -3,6 +3,7 @@ package exec import ( "bytes" "encoding/json" + "errors" "fmt" "os" "os/exec" @@ -103,7 +104,7 @@ func runCommandAndCaptureStdErr(command string, args []string, env []string, wor err := cmd.Run() if err != nil { - return portainer.Error(stderr.String()) + return errors.New(stderr.String()) } return nil diff --git a/api/filesystem/filesystem.go b/api/filesystem/filesystem.go index eb5024a93..ff99bd32c 100644 --- a/api/filesystem/filesystem.go +++ b/api/filesystem/filesystem.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "encoding/pem" + "errors" "fmt" "io/ioutil" @@ -50,6 +51,9 @@ const ( TempPath = "tmp" ) +// ErrUndefinedTLSFileType represents an error returned on undefined TLS file type +var ErrUndefinedTLSFileType = errors.New("Undefined TLS file type") + // Service represents a service for managing files and directories. type Service struct { dataStorePath string @@ -194,7 +198,7 @@ func (service *Service) StoreTLSFileFromBytes(folder string, fileType portainer. case portainer.TLSFileKey: fileName = TLSKeyFile default: - return "", portainer.ErrUndefinedTLSFileType + return "", ErrUndefinedTLSFileType } tlsFilePath := path.Join(storePath, fileName) @@ -217,7 +221,7 @@ func (service *Service) GetPathForTLSFile(folder string, fileType portainer.TLSF case portainer.TLSFileKey: fileName = TLSKeyFile default: - return "", portainer.ErrUndefinedTLSFileType + return "", ErrUndefinedTLSFileType } return path.Join(service.fileStorePath, TLSStorePath, folder, fileName), nil } @@ -243,7 +247,7 @@ func (service *Service) DeleteTLSFile(folder string, fileType portainer.TLSFileT case portainer.TLSFileKey: fileName = TLSKeyFile default: - return portainer.ErrUndefinedTLSFileType + return ErrUndefinedTLSFileType } filePath := path.Join(service.fileStorePath, TLSStorePath, folder, fileName) diff --git a/api/http/client/client.go b/api/http/client/client.go index fb690105f..ba185950a 100644 --- a/api/http/client/client.go +++ b/api/http/client/client.go @@ -3,6 +3,7 @@ package client import ( "crypto/tls" "encoding/json" + "errors" "fmt" "io/ioutil" "log" @@ -14,9 +15,10 @@ import ( "github.com/portainer/portainer/api" ) +var errInvalidResponseStatus = errors.New("Invalid response status (expecting 200)") + const ( - errInvalidResponseStatus = portainer.Error("Invalid response status (expecting 200)") - defaultHTTPTimeout = 5 + defaultHTTPTimeout = 5 ) // HTTPClient represents a client to send HTTP requests. @@ -56,7 +58,7 @@ func (client *HTTPClient) ExecuteAzureAuthenticationRequest(credentials *portain } if response.StatusCode != http.StatusOK { - return nil, portainer.ErrAzureInvalidCredentials + return nil, errors.New("Invalid Azure credentials") } var token AzureAuthenticationResponse diff --git a/api/http/errors/errors.go b/api/http/errors/errors.go new file mode 100644 index 000000000..2e6aeceb5 --- /dev/null +++ b/api/http/errors/errors.go @@ -0,0 +1,12 @@ +package errors + +import "errors" + +var ( + // ErrEndpointAccessDenied Access denied to endpoint error + ErrEndpointAccessDenied = errors.New("Access denied to endpoint") + // ErrUnauthorized Unauthorized error + ErrUnauthorized = errors.New("Unauthorized") + // ErrResourceAccessDenied Access denied to resource error + ErrResourceAccessDenied = errors.New("Access denied to resource") +) diff --git a/api/http/handler/auth/authenticate.go b/api/http/handler/auth/authenticate.go index eb9592566..52c1982b9 100644 --- a/api/http/handler/auth/authenticate.go +++ b/api/http/handler/auth/authenticate.go @@ -1,6 +1,7 @@ package auth import ( + "errors" "log" "net/http" "strings" @@ -10,6 +11,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/internal/authorization" ) @@ -24,10 +27,10 @@ type authenticateResponse struct { func (payload *authenticatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Username) { - return portainer.Error("Invalid username") + return errors.New("Invalid username") } if govalidator.IsNull(payload.Password) { - return portainer.Error("Invalid password") + return errors.New("Invalid password") } return nil } @@ -45,19 +48,19 @@ func (handler *Handler) authenticate(w http.ResponseWriter, r *http.Request) *ht } u, err := handler.DataStore.User().UserByUsername(payload.Username) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve a user with the specified username from the database", err} } - if err == portainer.ErrObjectNotFound && (settings.AuthenticationMethod == portainer.AuthenticationInternal || settings.AuthenticationMethod == portainer.AuthenticationOAuth) { - return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", portainer.ErrUnauthorized} + if err == bolterrors.ErrObjectNotFound && (settings.AuthenticationMethod == portainer.AuthenticationInternal || settings.AuthenticationMethod == portainer.AuthenticationOAuth) { + return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized} } if settings.AuthenticationMethod == portainer.AuthenticationLDAP { if u == nil && settings.LDAPSettings.AutoCreateUsers { return handler.authenticateLDAPAndCreateUser(w, payload.Username, payload.Password, &settings.LDAPSettings) } else if u == nil && !settings.LDAPSettings.AutoCreateUsers { - return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized} } return handler.authenticateLDAP(w, u, payload.Password, &settings.LDAPSettings) } @@ -87,7 +90,7 @@ func (handler *Handler) authenticateLDAP(w http.ResponseWriter, user *portainer. func (handler *Handler) authenticateInternal(w http.ResponseWriter, user *portainer.User, password string) *httperror.HandlerError { err := handler.CryptoService.CompareHashAndData(user.Password, password) if err != nil { - return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized} } return handler.writeToken(w, user) diff --git a/api/http/handler/auth/authenticate_oauth.go b/api/http/handler/auth/authenticate_oauth.go index 68f998495..f0d6d59a2 100644 --- a/api/http/handler/auth/authenticate_oauth.go +++ b/api/http/handler/auth/authenticate_oauth.go @@ -2,7 +2,7 @@ package auth import ( "encoding/json" - "github.com/portainer/portainer/api/internal/authorization" + "errors" "io/ioutil" "log" "net/http" @@ -11,6 +11,9 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" + "github.com/portainer/portainer/api/internal/authorization" ) type oauthPayload struct { @@ -19,7 +22,7 @@ type oauthPayload struct { func (payload *oauthPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Code) { - return portainer.Error("Invalid OAuth authorization code") + return errors.New("Invalid OAuth authorization code") } return nil } @@ -66,7 +69,7 @@ func (handler *Handler) authenticateThroughExtension(code, licenseKey string, se } if resp.StatusCode != http.StatusOK { - return "", portainer.Error(extResp.Err + ":" + extResp.Details) + return "", errors.New(extResp.Err + ":" + extResp.Details) } return extResp.Username, nil @@ -85,11 +88,11 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h } if settings.AuthenticationMethod != 3 { - return &httperror.HandlerError{http.StatusForbidden, "OAuth authentication is not enabled", portainer.Error("OAuth authentication is not enabled")} + return &httperror.HandlerError{http.StatusForbidden, "OAuth authentication is not enabled", errors.New("OAuth authentication is not enabled")} } extension, err := handler.DataStore.Extension().Extension(portainer.OAuthAuthenticationExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Oauth authentication extension is not enabled", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err} @@ -98,16 +101,16 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h username, err := handler.authenticateThroughExtension(payload.Code, extension.License.LicenseKey, &settings.OAuthSettings) if err != nil { log.Printf("[DEBUG] - OAuth authentication error: %s", err) - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate through OAuth", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate through OAuth", httperrors.ErrUnauthorized} } user, err := handler.DataStore.User().UserByUsername(username) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve a user with the specified username from the database", err} } if user == nil && !settings.OAuthSettings.OAuthAutoCreateUsers { - return &httperror.HandlerError{http.StatusForbidden, "Account not created beforehand in Portainer and automatic user provisioning not enabled", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Account not created beforehand in Portainer and automatic user provisioning not enabled", httperrors.ErrUnauthorized} } if user == nil { diff --git a/api/http/handler/dockerhub/dockerhub_update.go b/api/http/handler/dockerhub/dockerhub_update.go index 78787566e..e12d3ad24 100644 --- a/api/http/handler/dockerhub/dockerhub_update.go +++ b/api/http/handler/dockerhub/dockerhub_update.go @@ -1,6 +1,7 @@ package dockerhub import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -18,7 +19,7 @@ type dockerhubUpdatePayload struct { func (payload *dockerhubUpdatePayload) Validate(r *http.Request) error { if payload.Authentication && (govalidator.IsNull(payload.Username) || govalidator.IsNull(payload.Password)) { - return portainer.Error("Invalid credentials. Username and password must be specified when authentication is enabled") + return errors.New("Invalid credentials. Username and password must be specified when authentication is enabled") } return nil } diff --git a/api/http/handler/edgegroups/edgegroup_create.go b/api/http/handler/edgegroups/edgegroup_create.go index 90c733073..3e767891e 100644 --- a/api/http/handler/edgegroups/edgegroup_create.go +++ b/api/http/handler/edgegroups/edgegroup_create.go @@ -1,6 +1,7 @@ package edgegroups import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -20,13 +21,13 @@ type edgeGroupCreatePayload struct { func (payload *edgeGroupCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid Edge group name") + return errors.New("Invalid Edge group name") } if payload.Dynamic && (payload.TagIDs == nil || len(payload.TagIDs) == 0) { - return portainer.Error("TagIDs is mandatory for a dynamic Edge group") + return errors.New("TagIDs is mandatory for a dynamic Edge group") } if !payload.Dynamic && (payload.Endpoints == nil || len(payload.Endpoints) == 0) { - return portainer.Error("Endpoints is mandatory for a static Edge group") + return errors.New("Endpoints is mandatory for a static Edge group") } return nil } @@ -45,7 +46,7 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request) for _, edgeGroup := range edgeGroups { if edgeGroup.Name == payload.Name { - return &httperror.HandlerError{http.StatusBadRequest, "Edge group name must be unique", portainer.Error("Edge group name must be unique")} + return &httperror.HandlerError{http.StatusBadRequest, "Edge group name must be unique", errors.New("Edge group name must be unique")} } } diff --git a/api/http/handler/edgegroups/edgegroup_delete.go b/api/http/handler/edgegroups/edgegroup_delete.go index 806888617..a45486bfc 100644 --- a/api/http/handler/edgegroups/edgegroup_delete.go +++ b/api/http/handler/edgegroups/edgegroup_delete.go @@ -1,12 +1,14 @@ package edgegroups import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { @@ -16,7 +18,7 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request) } _, err = handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge group with the specified identifier inside the database", err} @@ -30,7 +32,7 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request) for _, edgeStack := range edgeStacks { for _, groupID := range edgeStack.EdgeGroups { if groupID == portainer.EdgeGroupID(edgeGroupID) { - return &httperror.HandlerError{http.StatusForbidden, "Edge group is used by an Edge stack", portainer.Error("Edge group is used by an Edge stack")} + return &httperror.HandlerError{http.StatusForbidden, "Edge group is used by an Edge stack", errors.New("Edge group is used by an Edge stack")} } } } diff --git a/api/http/handler/edgegroups/edgegroup_inspect.go b/api/http/handler/edgegroups/edgegroup_inspect.go index db88789c1..9f7f31173 100644 --- a/api/http/handler/edgegroups/edgegroup_inspect.go +++ b/api/http/handler/edgegroups/edgegroup_inspect.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { @@ -16,7 +17,7 @@ func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request) } edgeGroup, err := handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge group with the specified identifier inside the database", err} diff --git a/api/http/handler/edgegroups/edgegroup_update.go b/api/http/handler/edgegroups/edgegroup_update.go index 2be4fb346..e71f48457 100644 --- a/api/http/handler/edgegroups/edgegroup_update.go +++ b/api/http/handler/edgegroups/edgegroup_update.go @@ -1,6 +1,7 @@ package edgegroups import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/edge" ) @@ -21,13 +23,13 @@ type edgeGroupUpdatePayload struct { func (payload *edgeGroupUpdatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid Edge group name") + return errors.New("Invalid Edge group name") } if payload.Dynamic && (payload.TagIDs == nil || len(payload.TagIDs) == 0) { - return portainer.Error("TagIDs is mandatory for a dynamic Edge group") + return errors.New("TagIDs is mandatory for a dynamic Edge group") } if !payload.Dynamic && (payload.Endpoints == nil || len(payload.Endpoints) == 0) { - return portainer.Error("Endpoints is mandatory for a static Edge group") + return errors.New("Endpoints is mandatory for a static Edge group") } return nil } @@ -45,7 +47,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request) } edgeGroup, err := handler.DataStore.EdgeGroup().EdgeGroup(portainer.EdgeGroupID(edgeGroupID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge group with the specified identifier inside the database", err} @@ -58,7 +60,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request) } for _, edgeGroup := range edgeGroups { if edgeGroup.Name == payload.Name && edgeGroup.ID != portainer.EdgeGroupID(edgeGroupID) { - return &httperror.HandlerError{http.StatusBadRequest, "Edge group name must be unique", portainer.Error("Edge group name must be unique")} + return &httperror.HandlerError{http.StatusBadRequest, "Edge group name must be unique", errors.New("Edge group name must be unique")} } } diff --git a/api/http/handler/edgejobs/edgejob_create.go b/api/http/handler/edgejobs/edgejob_create.go index 33f91493a..e329316a7 100644 --- a/api/http/handler/edgejobs/edgejob_create.go +++ b/api/http/handler/edgejobs/edgejob_create.go @@ -41,7 +41,7 @@ type edgeJobCreateFromFileContentPayload struct { func (payload *edgeJobCreateFromFileContentPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid Edge job name") + return errors.New("Invalid Edge job name") } if !govalidator.Matches(payload.Name, `^[a-zA-Z0-9][a-zA-Z0-9_.-]*$`) { @@ -49,15 +49,15 @@ func (payload *edgeJobCreateFromFileContentPayload) Validate(r *http.Request) er } if govalidator.IsNull(payload.CronExpression) { - return portainer.Error("Invalid cron expression") + return errors.New("Invalid cron expression") } if payload.Endpoints == nil || len(payload.Endpoints) == 0 { - return portainer.Error("Invalid endpoints payload") + return errors.New("Invalid endpoints payload") } if govalidator.IsNull(payload.FileContent) { - return portainer.Error("Invalid script file content") + return errors.New("Invalid script file content") } return nil @@ -114,7 +114,7 @@ func (payload *edgeJobCreateFromFilePayload) Validate(r *http.Request) error { file, _, err := request.RetrieveMultiPartFormFile(r, "file") if err != nil { - return portainer.Error("Invalid script file. Ensure that the file is uploaded correctly") + return errors.New("Invalid script file. Ensure that the file is uploaded correctly") } payload.File = file diff --git a/api/http/handler/edgejobs/edgejob_delete.go b/api/http/handler/edgejobs/edgejob_delete.go index da3e39ac6..66efdd55e 100644 --- a/api/http/handler/edgejobs/edgejob_delete.go +++ b/api/http/handler/edgejobs/edgejob_delete.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { @@ -17,7 +18,7 @@ func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *h } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_file.go b/api/http/handler/edgejobs/edgejob_file.go index cd02f0919..25e71366a 100644 --- a/api/http/handler/edgejobs/edgejob_file.go +++ b/api/http/handler/edgejobs/edgejob_file.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type edgeJobFileResponse struct { @@ -21,7 +22,7 @@ func (handler *Handler) edgeJobFile(w http.ResponseWriter, r *http.Request) *htt } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_inspect.go b/api/http/handler/edgejobs/edgejob_inspect.go index 49857bb39..0625987cf 100644 --- a/api/http/handler/edgejobs/edgejob_inspect.go +++ b/api/http/handler/edgejobs/edgejob_inspect.go @@ -3,11 +3,11 @@ package edgejobs import ( "net/http" - "github.com/portainer/libhttp/response" - "github.com/portainer/portainer/api" - httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" + "github.com/portainer/libhttp/response" + "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type edgeJobInspectResponse struct { @@ -22,7 +22,7 @@ func (handler *Handler) edgeJobInspect(w http.ResponseWriter, r *http.Request) * } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_tasklogs_clear.go b/api/http/handler/edgejobs/edgejob_tasklogs_clear.go index c100260b3..e49f3c978 100644 --- a/api/http/handler/edgejobs/edgejob_tasklogs_clear.go +++ b/api/http/handler/edgejobs/edgejob_tasklogs_clear.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/edge_jobs/:id/tasks/:taskID/logs @@ -23,7 +24,7 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_tasklogs_collect.go b/api/http/handler/edgejobs/edgejob_tasklogs_collect.go index e19af696c..cb09aa2db 100644 --- a/api/http/handler/edgejobs/edgejob_tasklogs_collect.go +++ b/api/http/handler/edgejobs/edgejob_tasklogs_collect.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) // POST request on /api/edge_jobs/:id/tasks/:taskID/logs @@ -22,7 +23,7 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_tasks_list.go b/api/http/handler/edgejobs/edgejob_tasks_list.go index 57bbf3784..6b021255c 100644 --- a/api/http/handler/edgejobs/edgejob_tasks_list.go +++ b/api/http/handler/edgejobs/edgejob_tasks_list.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type taskContainer struct { @@ -18,22 +19,13 @@ type taskContainer struct { // GET request on /api/edge_jobs/:id/tasks func (handler *Handler) edgeJobTasksList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - settings, err := handler.DataStore.Settings().Settings() - if err != nil { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Unable to retrieve settings", err} - } - - if !settings.EnableEdgeComputeFeatures { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Edge compute features are disabled", portainer.ErrHostManagementFeaturesDisabled} - } - edgeJobID, err := request.RetrieveNumericRouteVariableValue(r, "id") if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid Edge job identifier route variable", err} } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgejobs/edgejob_update.go b/api/http/handler/edgejobs/edgejob_update.go index 559ba26f2..26d756320 100644 --- a/api/http/handler/edgejobs/edgejob_update.go +++ b/api/http/handler/edgejobs/edgejob_update.go @@ -10,6 +10,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type edgeJobUpdatePayload struct { @@ -28,15 +29,6 @@ func (payload *edgeJobUpdatePayload) Validate(r *http.Request) error { } func (handler *Handler) edgeJobUpdate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - settings, err := handler.DataStore.Settings().Settings() - if err != nil { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Unable to retrieve settings", err} - } - - if !settings.EnableEdgeComputeFeatures { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Edge compute features are disabled", portainer.ErrHostManagementFeaturesDisabled} - } - edgeJobID, err := request.RetrieveNumericRouteVariableValue(r, "id") if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid Edge job identifier route variable", err} @@ -49,7 +41,7 @@ func (handler *Handler) edgeJobUpdate(w http.ResponseWriter, r *http.Request) *h } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an Edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an Edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/edgestacks/edgestack_create.go b/api/http/handler/edgestacks/edgestack_create.go index e6e96f02d..0f3224e95 100644 --- a/api/http/handler/edgestacks/edgestack_create.go +++ b/api/http/handler/edgestacks/edgestack_create.go @@ -82,13 +82,13 @@ type swarmStackFromFileContentPayload struct { func (payload *swarmStackFromFileContentPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } if payload.EdgeGroups == nil || len(payload.EdgeGroups) == 0 { - return portainer.Error("Edge Groups are mandatory for an Edge stack") + return errors.New("Edge Groups are mandatory for an Edge stack") } return nil } @@ -144,19 +144,19 @@ type swarmStackFromGitRepositoryPayload struct { func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) { - return portainer.Error("Invalid repository URL. Must correspond to a valid URL format") + return errors.New("Invalid repository URL. Must correspond to a valid URL format") } if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { - return portainer.Error("Invalid repository credentials. Username and password must be specified when authentication is enabled") + return errors.New("Invalid repository credentials. Username and password must be specified when authentication is enabled") } if govalidator.IsNull(payload.ComposeFilePathInRepository) { payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName } if payload.EdgeGroups == nil || len(payload.EdgeGroups) == 0 { - return portainer.Error("Edge Groups are mandatory for an Edge stack") + return errors.New("Edge Groups are mandatory for an Edge stack") } return nil } @@ -218,20 +218,20 @@ type swarmStackFromFileUploadPayload struct { func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error { name, err := request.RetrieveMultiPartFormValue(r, "Name", false) if err != nil { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } payload.Name = name composeFileContent, _, err := request.RetrieveMultiPartFormFile(r, "file") if err != nil { - return portainer.Error("Invalid Compose file. Ensure that the Compose file is uploaded correctly") + return errors.New("Invalid Compose file. Ensure that the Compose file is uploaded correctly") } payload.StackFileContent = composeFileContent var edgeGroups []portainer.EdgeGroupID err = request.RetrieveMultiPartFormJSONValue(r, "EdgeGroups", &edgeGroups, false) if err != nil || len(edgeGroups) == 0 { - return portainer.Error("Edge Groups are mandatory for an Edge stack") + return errors.New("Edge Groups are mandatory for an Edge stack") } payload.EdgeGroups = edgeGroups return nil @@ -283,7 +283,7 @@ func (handler *Handler) validateUniqueName(name string) error { for _, stack := range edgeStacks { if strings.EqualFold(stack.Name, name) { - return portainer.Error("Edge stack name must be unique") + return errors.New("Edge stack name must be unique") } } return nil diff --git a/api/http/handler/edgestacks/edgestack_delete.go b/api/http/handler/edgestacks/edgestack_delete.go index 641d2d66e..ee01443f5 100644 --- a/api/http/handler/edgestacks/edgestack_delete.go +++ b/api/http/handler/edgestacks/edgestack_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/edge" ) @@ -17,7 +18,7 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request) } edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge stack with the specified identifier inside the database", err} diff --git a/api/http/handler/edgestacks/edgestack_file.go b/api/http/handler/edgestacks/edgestack_file.go index bcb20f626..d45dc433c 100644 --- a/api/http/handler/edgestacks/edgestack_file.go +++ b/api/http/handler/edgestacks/edgestack_file.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) type stackFileResponse struct { @@ -22,7 +23,7 @@ func (handler *Handler) edgeStackFile(w http.ResponseWriter, r *http.Request) *h } stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge stack with the specified identifier inside the database", err} diff --git a/api/http/handler/edgestacks/edgestack_inspect.go b/api/http/handler/edgestacks/edgestack_inspect.go index ae417e603..50a960817 100644 --- a/api/http/handler/edgestacks/edgestack_inspect.go +++ b/api/http/handler/edgestacks/edgestack_inspect.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) func (handler *Handler) edgeStackInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { @@ -16,7 +17,7 @@ func (handler *Handler) edgeStackInspect(w http.ResponseWriter, r *http.Request) } edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge stack with the specified identifier inside the database", err} diff --git a/api/http/handler/edgestacks/edgestack_status_update.go b/api/http/handler/edgestacks/edgestack_status_update.go index 5d5c3ebda..f1d870d51 100644 --- a/api/http/handler/edgestacks/edgestack_status_update.go +++ b/api/http/handler/edgestacks/edgestack_status_update.go @@ -1,6 +1,7 @@ package edgestacks import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type updateStatusPayload struct { @@ -18,13 +20,13 @@ type updateStatusPayload struct { func (payload *updateStatusPayload) Validate(r *http.Request) error { if payload.Status == nil { - return portainer.Error("Invalid status") + return errors.New("Invalid status") } if payload.EndpointID == nil { - return portainer.Error("Invalid EndpointID") + return errors.New("Invalid EndpointID") } if *payload.Status == portainer.StatusError && govalidator.IsNull(payload.Error) { - return portainer.Error("Error message is mandatory when status is error") + return errors.New("Error message is mandatory when status is error") } return nil } @@ -36,7 +38,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req } stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} @@ -49,7 +51,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(*payload.EndpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/edgestacks/edgestack_update.go b/api/http/handler/edgestacks/edgestack_update.go index 39edc92e1..ccc65bf44 100644 --- a/api/http/handler/edgestacks/edgestack_update.go +++ b/api/http/handler/edgestacks/edgestack_update.go @@ -1,6 +1,7 @@ package edgestacks import ( + "errors" "net/http" "strconv" @@ -9,6 +10,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/edge" ) @@ -21,10 +23,10 @@ type updateEdgeStackPayload struct { func (payload *updateEdgeStackPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } if payload.EdgeGroups != nil && len(payload.EdgeGroups) == 0 { - return portainer.Error("Edge Groups are mandatory for an Edge stack") + return errors.New("Edge Groups are mandatory for an Edge stack") } return nil } @@ -36,7 +38,7 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request) } stack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointedge/endpoint_edgejob_logs.go b/api/http/handler/endpointedge/endpoint_edgejob_logs.go index 8596ceeb5..d433f5789 100644 --- a/api/http/handler/endpointedge/endpoint_edgejob_logs.go +++ b/api/http/handler/endpointedge/endpoint_edgejob_logs.go @@ -7,7 +7,8 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type logsPayload struct { @@ -26,7 +27,7 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -49,7 +50,7 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ } edgeJob, err := handler.DataStore.EdgeJob().EdgeJob(portainer.EdgeJobID(edgeJobID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge job with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge job with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointedge/endpoint_edgestack_inspect.go b/api/http/handler/endpointedge/endpoint_edgestack_inspect.go index 54af3f3ef..9f57e115f 100644 --- a/api/http/handler/endpointedge/endpoint_edgestack_inspect.go +++ b/api/http/handler/endpointedge/endpoint_edgestack_inspect.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) type configResponse struct { @@ -24,7 +25,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http. } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -41,7 +42,7 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http. } edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an edge stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an edge stack with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointgroups/endpointgroup_create.go b/api/http/handler/endpointgroups/endpointgroup_create.go index d77464744..f50bf9736 100644 --- a/api/http/handler/endpointgroups/endpointgroup_create.go +++ b/api/http/handler/endpointgroups/endpointgroup_create.go @@ -1,6 +1,7 @@ package endpointgroups import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -19,7 +20,7 @@ type endpointGroupCreatePayload struct { func (payload *endpointGroupCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid endpoint group name") + return errors.New("Invalid endpoint group name") } if payload.TagIDs == nil { payload.TagIDs = []portainer.TagID{} diff --git a/api/http/handler/endpointgroups/endpointgroup_delete.go b/api/http/handler/endpointgroups/endpointgroup_delete.go index 0dba168eb..39ab4ba1e 100644 --- a/api/http/handler/endpointgroups/endpointgroup_delete.go +++ b/api/http/handler/endpointgroups/endpointgroup_delete.go @@ -1,12 +1,14 @@ package endpointgroups import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/endpoint_groups/:id @@ -17,11 +19,11 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque } if endpointGroupID == 1 { - return &httperror.HandlerError{http.StatusForbidden, "Unable to remove the default 'Unassigned' group", portainer.ErrCannotRemoveDefaultGroup} + return &httperror.HandlerError{http.StatusForbidden, "Unable to remove the default 'Unassigned' group", errors.New("Cannot remove the default endpoint group")} } endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointgroups/endpointgroup_endpoint_add.go b/api/http/handler/endpointgroups/endpointgroup_endpoint_add.go index 710aa7df5..cce2e737a 100644 --- a/api/http/handler/endpointgroups/endpointgroup_endpoint_add.go +++ b/api/http/handler/endpointgroups/endpointgroup_endpoint_add.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // PUT request on /api/endpoint_groups/:id/endpoints/:endpointId @@ -22,14 +23,14 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http. } endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err} } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointgroups/endpointgroup_endpoint_delete.go b/api/http/handler/endpointgroups/endpointgroup_endpoint_delete.go index f235b3ab9..595be6df9 100644 --- a/api/http/handler/endpointgroups/endpointgroup_endpoint_delete.go +++ b/api/http/handler/endpointgroups/endpointgroup_endpoint_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/endpoint_groups/:id/endpoints/:endpointId @@ -22,14 +23,14 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht } _, err = handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err} } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointgroups/endpointgroup_inspect.go b/api/http/handler/endpointgroups/endpointgroup_inspect.go index b931ba82b..fdf26a976 100644 --- a/api/http/handler/endpointgroups/endpointgroup_inspect.go +++ b/api/http/handler/endpointgroups/endpointgroup_inspect.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // GET request on /api/endpoint_groups/:id @@ -17,7 +18,7 @@ func (handler *Handler) endpointGroupInspect(w http.ResponseWriter, r *http.Requ } endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointgroups/endpointgroup_update.go b/api/http/handler/endpointgroups/endpointgroup_update.go index c03215518..72cf3b3f6 100644 --- a/api/http/handler/endpointgroups/endpointgroup_update.go +++ b/api/http/handler/endpointgroups/endpointgroup_update.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/tag" ) @@ -37,7 +38,7 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque } endpointGroup, err := handler.DataStore.EndpointGroup().EndpointGroup(portainer.EndpointGroupID(endpointGroupID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint group with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint group with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointproxy/proxy_azure.go b/api/http/handler/endpointproxy/proxy_azure.go index a9e66b66b..9984b763f 100644 --- a/api/http/handler/endpointproxy/proxy_azure.go +++ b/api/http/handler/endpointproxy/proxy_azure.go @@ -6,6 +6,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "net/http" ) @@ -17,7 +18,7 @@ func (handler *Handler) proxyRequestsToAzureAPI(w http.ResponseWriter, r *http.R } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointproxy/proxy_docker.go b/api/http/handler/endpointproxy/proxy_docker.go index 041a6f178..5b082e419 100644 --- a/api/http/handler/endpointproxy/proxy_docker.go +++ b/api/http/handler/endpointproxy/proxy_docker.go @@ -8,6 +8,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "net/http" ) @@ -19,7 +20,7 @@ func (handler *Handler) proxyRequestsToDockerAPI(w http.ResponseWriter, r *http. } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointproxy/proxy_kubernetes.go b/api/http/handler/endpointproxy/proxy_kubernetes.go index 744ab4340..be6400265 100644 --- a/api/http/handler/endpointproxy/proxy_kubernetes.go +++ b/api/http/handler/endpointproxy/proxy_kubernetes.go @@ -8,6 +8,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "net/http" ) @@ -19,7 +20,7 @@ func (handler *Handler) proxyRequestsToKubernetesAPI(w http.ResponseWriter, r *h } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpointproxy/proxy_storidge.go b/api/http/handler/endpointproxy/proxy_storidge.go index 70a019a83..e44a74abc 100644 --- a/api/http/handler/endpointproxy/proxy_storidge.go +++ b/api/http/handler/endpointproxy/proxy_storidge.go @@ -3,11 +3,13 @@ package endpointproxy // TODO: legacy extension management import ( + "errors" "strconv" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "net/http" ) @@ -19,7 +21,7 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -38,7 +40,7 @@ func (handler *Handler) proxyRequestsToStoridgeAPI(w http.ResponseWriter, r *htt } if storidgeExtension == nil { - return &httperror.HandlerError{http.StatusServiceUnavailable, "Storidge extension not supported on this endpoint", portainer.ErrEndpointExtensionNotSupported} + return &httperror.HandlerError{http.StatusServiceUnavailable, "Storidge extension not supported on this endpoint", errors.New("This extension is not supported")} } proxyExtensionKey := strconv.Itoa(endpointID) + "_" + strconv.Itoa(int(portainer.StoridgeEndpointExtension)) + "_" + storidgeExtension.URL diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index 9b8692903..210941fbc 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -39,13 +39,13 @@ type endpointCreatePayload struct { func (payload *endpointCreatePayload) Validate(r *http.Request) error { name, err := request.RetrieveMultiPartFormValue(r, "Name", false) if err != nil { - return portainer.Error("Invalid endpoint name") + return errors.New("Invalid endpoint name") } payload.Name = name endpointType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointType", false) if err != nil || endpointType == 0 { - return portainer.Error("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge Agent environment)") + return errors.New("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge Agent environment)") } payload.EndpointType = endpointType @@ -58,7 +58,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { var tagIDs []portainer.TagID err = request.RetrieveMultiPartFormJSONValue(r, "TagIds", &tagIDs, true) if err != nil { - return portainer.Error("Invalid TagIds parameter") + return errors.New("Invalid TagIds parameter") } payload.TagIDs = tagIDs if payload.TagIDs == nil { @@ -77,7 +77,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { if !payload.TLSSkipVerify { caCert, _, err := request.RetrieveMultiPartFormFile(r, "TLSCACertFile") if err != nil { - return portainer.Error("Invalid CA certificate file. Ensure that the file is uploaded correctly") + return errors.New("Invalid CA certificate file. Ensure that the file is uploaded correctly") } payload.TLSCACertFile = caCert } @@ -85,13 +85,13 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { if !payload.TLSSkipClientVerify { cert, _, err := request.RetrieveMultiPartFormFile(r, "TLSCertFile") if err != nil { - return portainer.Error("Invalid certificate file. Ensure that the file is uploaded correctly") + return errors.New("Invalid certificate file. Ensure that the file is uploaded correctly") } payload.TLSCertFile = cert key, _, err := request.RetrieveMultiPartFormFile(r, "TLSKeyFile") if err != nil { - return portainer.Error("Invalid key file. Ensure that the file is uploaded correctly") + return errors.New("Invalid key file. Ensure that the file is uploaded correctly") } payload.TLSKeyFile = key } @@ -101,25 +101,25 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { case portainer.AzureEnvironment: azureApplicationID, err := request.RetrieveMultiPartFormValue(r, "AzureApplicationID", false) if err != nil { - return portainer.Error("Invalid Azure application ID") + return errors.New("Invalid Azure application ID") } payload.AzureApplicationID = azureApplicationID azureTenantID, err := request.RetrieveMultiPartFormValue(r, "AzureTenantID", false) if err != nil { - return portainer.Error("Invalid Azure tenant ID") + return errors.New("Invalid Azure tenant ID") } payload.AzureTenantID = azureTenantID azureAuthenticationKey, err := request.RetrieveMultiPartFormValue(r, "AzureAuthenticationKey", false) if err != nil { - return portainer.Error("Invalid Azure authentication key") + return errors.New("Invalid Azure authentication key") } payload.AzureAuthenticationKey = azureAuthenticationKey default: endpointURL, err := request.RetrieveMultiPartFormValue(r, "URL", true) if err != nil { - return portainer.Error("Invalid endpoint URL") + return errors.New("Invalid endpoint URL") } payload.URL = endpointURL diff --git a/api/http/handler/endpoints/endpoint_delete.go b/api/http/handler/endpoints/endpoint_delete.go index 7a280d703..b35fc8cf2 100644 --- a/api/http/handler/endpoints/endpoint_delete.go +++ b/api/http/handler/endpoints/endpoint_delete.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/endpoints/:id @@ -18,7 +19,7 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) * } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_extension_add.go b/api/http/handler/endpoints/endpoint_extension_add.go index 4472913dc..e99e10caa 100644 --- a/api/http/handler/endpoints/endpoint_extension_add.go +++ b/api/http/handler/endpoints/endpoint_extension_add.go @@ -3,6 +3,7 @@ package endpoints // TODO: legacy extension management import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -10,6 +11,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type endpointExtensionAddPayload struct { @@ -19,10 +21,10 @@ type endpointExtensionAddPayload struct { func (payload *endpointExtensionAddPayload) Validate(r *http.Request) error { if payload.Type != 1 { - return portainer.Error("Invalid type value. Value must be one of: 1 (Storidge)") + return errors.New("Invalid type value. Value must be one of: 1 (Storidge)") } if payload.Type == 1 && govalidator.IsNull(payload.URL) { - return portainer.Error("Invalid extension URL") + return errors.New("Invalid extension URL") } return nil } @@ -35,7 +37,7 @@ func (handler *Handler) endpointExtensionAdd(w http.ResponseWriter, r *http.Requ } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_extension_remove.go b/api/http/handler/endpoints/endpoint_extension_remove.go index 6d81da363..99edf1bc8 100644 --- a/api/http/handler/endpoints/endpoint_extension_remove.go +++ b/api/http/handler/endpoints/endpoint_extension_remove.go @@ -9,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/endpoints/:id/extensions/:extensionType @@ -19,7 +20,7 @@ func (handler *Handler) endpointExtensionRemove(w http.ResponseWriter, r *http.R } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_inspect.go b/api/http/handler/endpoints/endpoint_inspect.go index 01abe2c3d..1ce758eb9 100644 --- a/api/http/handler/endpoints/endpoint_inspect.go +++ b/api/http/handler/endpoints/endpoint_inspect.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // GET request on /api/endpoints/:id @@ -17,7 +18,7 @@ func (handler *Handler) endpointInspect(w http.ResponseWriter, r *http.Request) } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_snapshot.go b/api/http/handler/endpoints/endpoint_snapshot.go index 03816b642..e834fea5f 100644 --- a/api/http/handler/endpoints/endpoint_snapshot.go +++ b/api/http/handler/endpoints/endpoint_snapshot.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/snapshot" ) @@ -18,7 +19,7 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request) } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_status_inspect.go b/api/http/handler/endpoints/endpoint_status_inspect.go index b29a1da5b..8c1dacd62 100644 --- a/api/http/handler/endpoints/endpoint_status_inspect.go +++ b/api/http/handler/endpoints/endpoint_status_inspect.go @@ -7,7 +7,8 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) type stackStatusResponse struct { @@ -40,7 +41,7 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index 8979571b0..305a57e54 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -9,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/http/client" "github.com/portainer/portainer/api/internal/edge" "github.com/portainer/portainer/api/internal/tag" @@ -51,7 +52,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/extensions/extension_create.go b/api/http/handler/extensions/extension_create.go index dd602f3b0..8d7add8c4 100644 --- a/api/http/handler/extensions/extension_create.go +++ b/api/http/handler/extensions/extension_create.go @@ -1,6 +1,7 @@ package extensions import ( + "errors" "net/http" "strconv" @@ -17,7 +18,7 @@ type extensionCreatePayload struct { func (payload *extensionCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.License) { - return portainer.Error("Invalid license") + return errors.New("Invalid license") } return nil @@ -43,7 +44,7 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request) for _, existingExtension := range extensions { if existingExtension.ID == extensionID && existingExtension.Enabled { - return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled} + return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", errors.New("This extension is already enabled")} } } diff --git a/api/http/handler/extensions/extension_delete.go b/api/http/handler/extensions/extension_delete.go index 789fa84fd..351de2406 100644 --- a/api/http/handler/extensions/extension_delete.go +++ b/api/http/handler/extensions/extension_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/extensions/:id @@ -18,7 +19,7 @@ func (handler *Handler) extensionDelete(w http.ResponseWriter, r *http.Request) extensionID := portainer.ExtensionID(extensionIdentifier) extension, err := handler.DataStore.Extension().Extension(extensionID) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a extension with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err} diff --git a/api/http/handler/extensions/extension_inspect.go b/api/http/handler/extensions/extension_inspect.go index 94c8292fe..4081a3cb7 100644 --- a/api/http/handler/extensions/extension_inspect.go +++ b/api/http/handler/extensions/extension_inspect.go @@ -3,12 +3,12 @@ package extensions import ( "net/http" - "github.com/portainer/portainer/api/http/client" - httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/client" ) // GET request on /api/extensions/:id @@ -26,7 +26,7 @@ func (handler *Handler) extensionInspect(w http.ResponseWriter, r *http.Request) } localExtension, err := handler.DataStore.Extension().Extension(extensionID) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extension information from the database", err} } diff --git a/api/http/handler/extensions/extension_update.go b/api/http/handler/extensions/extension_update.go index 38487a2f7..6ff469e48 100644 --- a/api/http/handler/extensions/extension_update.go +++ b/api/http/handler/extensions/extension_update.go @@ -1,6 +1,7 @@ package extensions import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type extensionUpdatePayload struct { @@ -16,7 +18,7 @@ type extensionUpdatePayload struct { func (payload *extensionUpdatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Version) { - return portainer.Error("Invalid extension version") + return errors.New("Invalid extension version") } return nil @@ -36,7 +38,7 @@ func (handler *Handler) extensionUpdate(w http.ResponseWriter, r *http.Request) } extension, err := handler.DataStore.Extension().Extension(extensionID) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a extension with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err} diff --git a/api/http/handler/extensions/extension_upload.go b/api/http/handler/extensions/extension_upload.go index 9469726be..236d445ee 100644 --- a/api/http/handler/extensions/extension_upload.go +++ b/api/http/handler/extensions/extension_upload.go @@ -1,6 +1,7 @@ package extensions import ( + "errors" "net/http" "strconv" @@ -19,13 +20,13 @@ type extensionUploadPayload struct { func (payload *extensionUploadPayload) Validate(r *http.Request) error { license, err := request.RetrieveMultiPartFormValue(r, "License", false) if err != nil { - return portainer.Error("Invalid license") + return errors.New("Invalid license") } payload.License = license fileData, fileName, err := request.RetrieveMultiPartFormFile(r, "file") if err != nil { - return portainer.Error("Invalid extension archive file. Ensure that the file is uploaded correctly") + return errors.New("Invalid extension archive file. Ensure that the file is uploaded correctly") } payload.ExtensionArchive = fileData payload.ArchiveFileName = fileName diff --git a/api/http/handler/registries/proxy.go b/api/http/handler/registries/proxy.go index 0bc996d95..d452e8e66 100644 --- a/api/http/handler/registries/proxy.go +++ b/api/http/handler/registries/proxy.go @@ -8,6 +8,8 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" ) // request on /api/registries/:id/v2 @@ -18,7 +20,7 @@ func (handler *Handler) proxyRequestsToRegistryAPI(w http.ResponseWriter, r *htt } registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} @@ -26,11 +28,11 @@ func (handler *Handler) proxyRequestsToRegistryAPI(w http.ResponseWriter, r *htt err = handler.requestBouncer.RegistryAccess(r, registry) if err != nil { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", portainer.ErrEndpointAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", errors.ErrEndpointAccessDenied} } extension, err := handler.DataStore.Extension().Extension(portainer.RegistryManagementExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Registry management extension is not enabled", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err} diff --git a/api/http/handler/registries/proxy_management_gitlab.go b/api/http/handler/registries/proxy_management_gitlab.go index c9dcbfac0..6a8656c38 100644 --- a/api/http/handler/registries/proxy_management_gitlab.go +++ b/api/http/handler/registries/proxy_management_gitlab.go @@ -8,6 +8,8 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" ) // request on /api/registries/{id}/proxies/gitlab @@ -18,7 +20,7 @@ func (handler *Handler) proxyRequestsToGitlabAPIWithRegistry(w http.ResponseWrit } registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} @@ -26,11 +28,11 @@ func (handler *Handler) proxyRequestsToGitlabAPIWithRegistry(w http.ResponseWrit err = handler.requestBouncer.RegistryAccess(r, registry) if err != nil { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", portainer.ErrEndpointAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", errors.ErrEndpointAccessDenied} } extension, err := handler.DataStore.Extension().Extension(portainer.RegistryManagementExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Registry management extension is not enabled", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a extension with the specified identifier inside the database", err} diff --git a/api/http/handler/registries/registry_configure.go b/api/http/handler/registries/registry_configure.go index 21d2f92f8..16bc4a30f 100644 --- a/api/http/handler/registries/registry_configure.go +++ b/api/http/handler/registries/registry_configure.go @@ -1,6 +1,7 @@ package registries import ( + "errors" "net/http" "strconv" @@ -8,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type registryConfigurePayload struct { @@ -28,7 +30,7 @@ func (payload *registryConfigurePayload) Validate(r *http.Request) error { if useAuthentication { username, err := request.RetrieveMultiPartFormValue(r, "Username", false) if err != nil { - return portainer.Error("Invalid username") + return errors.New("Invalid username") } payload.Username = username @@ -45,19 +47,19 @@ func (payload *registryConfigurePayload) Validate(r *http.Request) error { if useTLS && !skipTLSVerify { cert, _, err := request.RetrieveMultiPartFormFile(r, "TLSCertFile") if err != nil { - return portainer.Error("Invalid certificate file. Ensure that the file is uploaded correctly") + return errors.New("Invalid certificate file. Ensure that the file is uploaded correctly") } payload.TLSCertFile = cert key, _, err := request.RetrieveMultiPartFormFile(r, "TLSKeyFile") if err != nil { - return portainer.Error("Invalid key file. Ensure that the file is uploaded correctly") + return errors.New("Invalid key file. Ensure that the file is uploaded correctly") } payload.TLSKeyFile = key ca, _, err := request.RetrieveMultiPartFormFile(r, "TLSCACertFile") if err != nil { - return portainer.Error("Invalid CA certificate file. Ensure that the file is uploaded correctly") + return errors.New("Invalid CA certificate file. Ensure that the file is uploaded correctly") } payload.TLSCACertFile = ca } @@ -79,7 +81,7 @@ func (handler *Handler) registryConfigure(w http.ResponseWriter, r *http.Request } registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} diff --git a/api/http/handler/registries/registry_create.go b/api/http/handler/registries/registry_create.go index aac899138..fc0444e42 100644 --- a/api/http/handler/registries/registry_create.go +++ b/api/http/handler/registries/registry_create.go @@ -1,6 +1,7 @@ package registries import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -22,16 +23,16 @@ type registryCreatePayload struct { func (payload *registryCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid registry name") + return errors.New("Invalid registry name") } if govalidator.IsNull(payload.URL) { - return portainer.Error("Invalid registry URL") + return errors.New("Invalid registry URL") } if payload.Authentication && (govalidator.IsNull(payload.Username) || govalidator.IsNull(payload.Password)) { - return portainer.Error("Invalid credentials. Username and password must be specified when authentication is enabled") + return errors.New("Invalid credentials. Username and password must be specified when authentication is enabled") } if payload.Type != portainer.QuayRegistry && payload.Type != portainer.AzureRegistry && payload.Type != portainer.CustomRegistry && payload.Type != portainer.GitlabRegistry { - return portainer.Error("Invalid registry type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry) or 4 (Gitlab registry)") + return errors.New("Invalid registry type. Valid values are: 1 (Quay.io), 2 (Azure container registry), 3 (custom registry) or 4 (Gitlab registry)") } return nil } diff --git a/api/http/handler/registries/registry_delete.go b/api/http/handler/registries/registry_delete.go index 62e0c663f..877ca4a39 100644 --- a/api/http/handler/registries/registry_delete.go +++ b/api/http/handler/registries/registry_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/registries/:id @@ -17,7 +18,7 @@ func (handler *Handler) registryDelete(w http.ResponseWriter, r *http.Request) * } _, err = handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} diff --git a/api/http/handler/registries/registry_inspect.go b/api/http/handler/registries/registry_inspect.go index 586f198db..715e7dcc0 100644 --- a/api/http/handler/registries/registry_inspect.go +++ b/api/http/handler/registries/registry_inspect.go @@ -3,6 +3,9 @@ package registries import ( "net/http" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" + httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" @@ -17,7 +20,7 @@ func (handler *Handler) registryInspect(w http.ResponseWriter, r *http.Request) } registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} @@ -25,7 +28,7 @@ func (handler *Handler) registryInspect(w http.ResponseWriter, r *http.Request) err = handler.requestBouncer.RegistryAccess(r, registry) if err != nil { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", portainer.ErrEndpointAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access registry", errors.ErrEndpointAccessDenied} } hideFields(registry) diff --git a/api/http/handler/registries/registry_update.go b/api/http/handler/registries/registry_update.go index 1ba467199..e77dfb765 100644 --- a/api/http/handler/registries/registry_update.go +++ b/api/http/handler/registries/registry_update.go @@ -1,12 +1,14 @@ package registries import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type registryUpdatePayload struct { @@ -37,7 +39,7 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) * } registry, err := handler.DataStore.Registry().Registry(portainer.RegistryID(registryID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a registry with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a registry with the specified identifier inside the database", err} @@ -54,7 +56,7 @@ func (handler *Handler) registryUpdate(w http.ResponseWriter, r *http.Request) * } for _, r := range registries { if r.ID != registry.ID && hasSameURL(&r, registry) { - return &httperror.HandlerError{http.StatusConflict, "Another registry with the same URL already exists", portainer.ErrRegistryAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "Another registry with the same URL already exists", errors.New("A registry is already defined for this URL")} } } diff --git a/api/http/handler/resourcecontrols/resourcecontrol_create.go b/api/http/handler/resourcecontrols/resourcecontrol_create.go index 5f56daa19..0371c6f8c 100644 --- a/api/http/handler/resourcecontrols/resourcecontrol_create.go +++ b/api/http/handler/resourcecontrols/resourcecontrol_create.go @@ -21,6 +21,11 @@ type resourceControlCreatePayload struct { SubResourceIDs []string } +var ( + errResourceControlAlreadyExists = errors.New("A resource control is already applied on this resource") //http/resourceControl + errInvalidResourceControlType = errors.New("Unsupported resource control type") //http/resourceControl +) + func (payload *resourceControlCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.ResourceID) { return errors.New("invalid payload: invalid resource identifier") @@ -65,7 +70,7 @@ func (handler *Handler) resourceControlCreate(w http.ResponseWriter, r *http.Req case "config": resourceControlType = portainer.ConfigResourceControl default: - return &httperror.HandlerError{http.StatusBadRequest, "Invalid type value. Value must be one of: container, service, volume, network, secret, stack or config", portainer.ErrInvalidResourceControlType} + return &httperror.HandlerError{http.StatusBadRequest, "Invalid type value. Value must be one of: container, service, volume, network, secret, stack or config", errInvalidResourceControlType} } rc, err := handler.DataStore.ResourceControl().ResourceControlByResourceIDAndType(payload.ResourceID, resourceControlType) @@ -73,7 +78,7 @@ func (handler *Handler) resourceControlCreate(w http.ResponseWriter, r *http.Req return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve resource controls from the database", err} } if rc != nil { - return &httperror.HandlerError{http.StatusConflict, "A resource control is already associated to this resource", portainer.ErrResourceControlAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A resource control is already associated to this resource", errResourceControlAlreadyExists} } var userAccesses = make([]portainer.UserResourceAccess, 0) diff --git a/api/http/handler/resourcecontrols/resourcecontrol_delete.go b/api/http/handler/resourcecontrols/resourcecontrol_delete.go index 076e86519..394974923 100644 --- a/api/http/handler/resourcecontrols/resourcecontrol_delete.go +++ b/api/http/handler/resourcecontrols/resourcecontrol_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/resource_controls/:id @@ -17,7 +18,7 @@ func (handler *Handler) resourceControlDelete(w http.ResponseWriter, r *http.Req } _, err = handler.DataStore.ResourceControl().ResourceControl(portainer.ResourceControlID(resourceControlID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a resource control with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a resource control with with the specified identifier inside the database", err} diff --git a/api/http/handler/resourcecontrols/resourcecontrol_update.go b/api/http/handler/resourcecontrols/resourcecontrol_update.go index 9e5f52752..b200a290d 100644 --- a/api/http/handler/resourcecontrols/resourcecontrol_update.go +++ b/api/http/handler/resourcecontrols/resourcecontrol_update.go @@ -8,6 +8,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -43,7 +45,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req } resourceControl, err := handler.DataStore.ResourceControl().ResourceControl(portainer.ResourceControlID(resourceControlID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a resource control with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a resource control with with the specified identifier inside the database", err} @@ -55,7 +57,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req } if !security.AuthorizedResourceControlAccess(resourceControl, securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access the resource control", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access the resource control", httperrors.ErrResourceAccessDenied} } resourceControl.Public = payload.Public @@ -82,7 +84,7 @@ func (handler *Handler) resourceControlUpdate(w http.ResponseWriter, r *http.Req resourceControl.TeamAccesses = teamAccesses if !security.AuthorizedResourceControlUpdate(resourceControl, securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the resource control", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the resource control", httperrors.ErrResourceAccessDenied} } err = handler.DataStore.ResourceControl().UpdateResourceControl(resourceControl.ID, resourceControl) diff --git a/api/http/handler/settings/settings_update.go b/api/http/handler/settings/settings_update.go index 79047e146..d0a7f896a 100644 --- a/api/http/handler/settings/settings_update.go +++ b/api/http/handler/settings/settings_update.go @@ -1,6 +1,7 @@ package settings import ( + "errors" "net/http" "time" @@ -9,6 +10,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/filesystem" ) @@ -31,18 +33,18 @@ type settingsUpdatePayload struct { func (payload *settingsUpdatePayload) Validate(r *http.Request) error { if *payload.AuthenticationMethod != 1 && *payload.AuthenticationMethod != 2 && *payload.AuthenticationMethod != 3 { - return portainer.Error("Invalid authentication method value. Value must be one of: 1 (internal), 2 (LDAP/AD) or 3 (OAuth)") + return errors.New("Invalid authentication method value. Value must be one of: 1 (internal), 2 (LDAP/AD) or 3 (OAuth)") } if payload.LogoURL != nil && *payload.LogoURL != "" && !govalidator.IsURL(*payload.LogoURL) { - return portainer.Error("Invalid logo URL. Must correspond to a valid URL format") + return errors.New("Invalid logo URL. Must correspond to a valid URL format") } if payload.TemplatesURL != nil && *payload.TemplatesURL != "" && !govalidator.IsURL(*payload.TemplatesURL) { - return portainer.Error("Invalid external templates URL. Must correspond to a valid URL format") + return errors.New("Invalid external templates URL. Must correspond to a valid URL format") } if payload.UserSessionTimeout != nil { _, err := time.ParseDuration(*payload.UserSessionTimeout) if err != nil { - return portainer.Error("Invalid user session timeout") + return errors.New("Invalid user session timeout") } } @@ -169,7 +171,7 @@ func (handler *Handler) updateVolumeBrowserSetting(settings *portainer.Settings) } extension, err := handler.DataStore.Extension().Extension(portainer.RBACExtension) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return err } diff --git a/api/http/handler/stacks/create_compose_stack.go b/api/http/handler/stacks/create_compose_stack.go index 051999980..b021190b8 100644 --- a/api/http/handler/stacks/create_compose_stack.go +++ b/api/http/handler/stacks/create_compose_stack.go @@ -31,11 +31,11 @@ type composeStackFromFileContentPayload struct { func (payload *composeStackFromFileContentPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } payload.Name = normalizeStackName(payload.Name) if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } return nil } @@ -54,7 +54,7 @@ func (handler *Handler) createComposeStackFromFileContent(w http.ResponseWriter, for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } @@ -110,14 +110,14 @@ type composeStackFromGitRepositoryPayload struct { func (payload *composeStackFromGitRepositoryPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } payload.Name = normalizeStackName(payload.Name) if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) { - return portainer.Error("Invalid repository URL. Must correspond to a valid URL format") + return errors.New("Invalid repository URL. Must correspond to a valid URL format") } if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { - return portainer.Error("Invalid repository credentials. Username and password must be specified when authentication is enabled") + return errors.New("Invalid repository credentials. Username and password must be specified when authentication is enabled") } if govalidator.IsNull(payload.ComposeFilePathInRepository) { payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName @@ -139,7 +139,7 @@ func (handler *Handler) createComposeStackFromGitRepository(w http.ResponseWrite for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } @@ -201,20 +201,20 @@ type composeStackFromFileUploadPayload struct { func (payload *composeStackFromFileUploadPayload) Validate(r *http.Request) error { name, err := request.RetrieveMultiPartFormValue(r, "Name", false) if err != nil { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } payload.Name = normalizeStackName(name) composeFileContent, _, err := request.RetrieveMultiPartFormFile(r, "file") if err != nil { - return portainer.Error("Invalid Compose file. Ensure that the Compose file is uploaded correctly") + return errors.New("Invalid Compose file. Ensure that the Compose file is uploaded correctly") } payload.StackFileContent = composeFileContent var env []portainer.Pair err = request.RetrieveMultiPartFormJSONValue(r, "Env", &env, true) if err != nil { - return portainer.Error("Invalid Env parameter") + return errors.New("Invalid Env parameter") } payload.Env = env return nil @@ -234,7 +234,7 @@ func (handler *Handler) createComposeStackFromFileUpload(w http.ResponseWriter, for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } diff --git a/api/http/handler/stacks/create_kubernetes_stack.go b/api/http/handler/stacks/create_kubernetes_stack.go index cbf8eb1fe..61ca665a5 100644 --- a/api/http/handler/stacks/create_kubernetes_stack.go +++ b/api/http/handler/stacks/create_kubernetes_stack.go @@ -1,6 +1,7 @@ package stacks import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -19,10 +20,10 @@ type kubernetesStackPayload struct { func (payload *kubernetesStackPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } if govalidator.IsNull(payload.Namespace) { - return portainer.Error("Invalid namespace") + return errors.New("Invalid namespace") } return nil } diff --git a/api/http/handler/stacks/create_swarm_stack.go b/api/http/handler/stacks/create_swarm_stack.go index 09e743e44..e28261bed 100644 --- a/api/http/handler/stacks/create_swarm_stack.go +++ b/api/http/handler/stacks/create_swarm_stack.go @@ -24,13 +24,13 @@ type swarmStackFromFileContentPayload struct { func (payload *swarmStackFromFileContentPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } if govalidator.IsNull(payload.SwarmID) { - return portainer.Error("Invalid Swarm ID") + return errors.New("Invalid Swarm ID") } if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } return nil } @@ -49,7 +49,7 @@ func (handler *Handler) createSwarmStackFromFileContent(w http.ResponseWriter, r for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } @@ -107,16 +107,16 @@ type swarmStackFromGitRepositoryPayload struct { func (payload *swarmStackFromGitRepositoryPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } if govalidator.IsNull(payload.SwarmID) { - return portainer.Error("Invalid Swarm ID") + return errors.New("Invalid Swarm ID") } if govalidator.IsNull(payload.RepositoryURL) || !govalidator.IsURL(payload.RepositoryURL) { - return portainer.Error("Invalid repository URL. Must correspond to a valid URL format") + return errors.New("Invalid repository URL. Must correspond to a valid URL format") } if payload.RepositoryAuthentication && (govalidator.IsNull(payload.RepositoryUsername) || govalidator.IsNull(payload.RepositoryPassword)) { - return portainer.Error("Invalid repository credentials. Username and password must be specified when authentication is enabled") + return errors.New("Invalid repository credentials. Username and password must be specified when authentication is enabled") } if govalidator.IsNull(payload.ComposeFilePathInRepository) { payload.ComposeFilePathInRepository = filesystem.ComposeFileDefaultName @@ -138,7 +138,7 @@ func (handler *Handler) createSwarmStackFromGitRepository(w http.ResponseWriter, for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } @@ -202,26 +202,26 @@ type swarmStackFromFileUploadPayload struct { func (payload *swarmStackFromFileUploadPayload) Validate(r *http.Request) error { name, err := request.RetrieveMultiPartFormValue(r, "Name", false) if err != nil { - return portainer.Error("Invalid stack name") + return errors.New("Invalid stack name") } payload.Name = name swarmID, err := request.RetrieveMultiPartFormValue(r, "SwarmID", false) if err != nil { - return portainer.Error("Invalid Swarm ID") + return errors.New("Invalid Swarm ID") } payload.SwarmID = swarmID composeFileContent, _, err := request.RetrieveMultiPartFormFile(r, "file") if err != nil { - return portainer.Error("Invalid Compose file. Ensure that the Compose file is uploaded correctly") + return errors.New("Invalid Compose file. Ensure that the Compose file is uploaded correctly") } payload.StackFileContent = composeFileContent var env []portainer.Pair err = request.RetrieveMultiPartFormJSONValue(r, "Env", &env, true) if err != nil { - return portainer.Error("Invalid Env parameter") + return errors.New("Invalid Env parameter") } payload.Env = env return nil @@ -241,7 +241,7 @@ func (handler *Handler) createSwarmStackFromFileUpload(w http.ResponseWriter, r for _, stack := range stacks { if strings.EqualFold(stack.Name, payload.Name) { - return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", portainer.ErrStackAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A stack with this name already exists", errStackAlreadyExists} } } diff --git a/api/http/handler/stacks/handler.go b/api/http/handler/stacks/handler.go index 5271cffb1..e560392c3 100644 --- a/api/http/handler/stacks/handler.go +++ b/api/http/handler/stacks/handler.go @@ -1,16 +1,23 @@ package stacks import ( + "errors" "net/http" "sync" "github.com/gorilla/mux" httperror "github.com/portainer/libhttp/error" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" ) +var ( + errStackAlreadyExists = errors.New("A stack already exists with this name") + errStackNotExternal = errors.New("Not an external stack") +) + // Handler is the HTTP handler used to handle stack operations. type Handler struct { stackCreationMutex *sync.Mutex @@ -65,9 +72,9 @@ func (handler *Handler) userCanAccessStack(securityContext *security.RestrictedR } _, err := handler.DataStore.Extension().Extension(portainer.RBACExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return false, nil - } else if err != nil && err != portainer.ErrObjectNotFound { + } else if err != nil && err != bolterrors.ErrObjectNotFound { return false, err } diff --git a/api/http/handler/stacks/stack_create.go b/api/http/handler/stacks/stack_create.go index b4a3a3992..87f358abd 100644 --- a/api/http/handler/stacks/stack_create.go +++ b/api/http/handler/stacks/stack_create.go @@ -11,6 +11,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" ) @@ -45,7 +47,7 @@ func (handler *Handler) stackCreate(w http.ResponseWriter, r *http.Request) *htt } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -68,7 +70,7 @@ func (handler *Handler) stackCreate(w http.ResponseWriter, r *http.Request) *htt return handler.createComposeStack(w, r, method, endpoint, tokenData.ID) case portainer.KubernetesStack: if tokenData.Role != portainer.AdministratorRole { - return &httperror.HandlerError{http.StatusForbidden, "Access denied", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Access denied", httperrors.ErrUnauthorized} } return handler.createKubernetesStack(w, r, endpoint) diff --git a/api/http/handler/stacks/stack_delete.go b/api/http/handler/stacks/stack_delete.go index cac9b21fe..89c7ab14b 100644 --- a/api/http/handler/stacks/stack_delete.go +++ b/api/http/handler/stacks/stack_delete.go @@ -1,15 +1,17 @@ package stacks import ( + "errors" "net/http" "strconv" - "github.com/portainer/portainer/api/http/security" - httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" portainer "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" + "github.com/portainer/portainer/api/http/security" ) // DELETE request on /api/stacks/:id?external=&endpointId= @@ -37,7 +39,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt } stack, err := handler.DataStore.Stack().Stack(portainer.StackID(id)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} @@ -57,7 +59,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt } endpoint, err := handler.DataStore.Endpoint().Endpoint(endpointIdentifier) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} @@ -78,7 +80,7 @@ func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *htt return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify user authorizations to validate stack access", err} } if !access { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", httperrors.ErrResourceAccessDenied} } err = handler.deleteStack(stack, endpoint) @@ -118,7 +120,7 @@ func (handler *Handler) deleteExternalStack(r *http.Request, w http.ResponseWrit } rbacExtension, err := handler.DataStore.Extension().Extension(portainer.RBACExtension) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify if RBAC extension is loaded", err} } @@ -130,24 +132,24 @@ func (handler *Handler) deleteExternalStack(r *http.Request, w http.ResponseWrit if rbacExtension != nil { if !securityContext.IsAdmin && !endpointResourceAccess { - return &httperror.HandlerError{http.StatusUnauthorized, "Permission denied to delete the stack", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusUnauthorized, "Permission denied to delete the stack", httperrors.ErrUnauthorized} } } else { if !securityContext.IsAdmin { - return &httperror.HandlerError{http.StatusUnauthorized, "Permission denied to delete the stack", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusUnauthorized, "Permission denied to delete the stack", httperrors.ErrUnauthorized} } } stack, err := handler.DataStore.Stack().StackByName(stackName) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to check for stack existence inside the database", err} } if stack != nil { - return &httperror.HandlerError{http.StatusBadRequest, "A stack with this name exists inside the database. Cannot use external delete method", portainer.ErrStackNotExternal} + return &httperror.HandlerError{http.StatusBadRequest, "A stack with this name exists inside the database. Cannot use external delete method", errors.New("A tag already exists with this name")} } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} diff --git a/api/http/handler/stacks/stack_file.go b/api/http/handler/stacks/stack_file.go index 7c601a263..e3a111668 100644 --- a/api/http/handler/stacks/stack_file.go +++ b/api/http/handler/stacks/stack_file.go @@ -8,6 +8,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -23,14 +25,14 @@ func (handler *Handler) stackFile(w http.ResponseWriter, r *http.Request) *httpe } stack, err := handler.DataStore.Stack().Stack(portainer.StackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} } endpoint, err := handler.DataStore.Endpoint().Endpoint(stack.EndpointID) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -56,7 +58,7 @@ func (handler *Handler) stackFile(w http.ResponseWriter, r *http.Request) *httpe return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify user authorizations to validate stack access", err} } if !access { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", errors.ErrResourceAccessDenied} } stackFileContent, err := handler.FileService.GetFileContent(path.Join(stack.ProjectPath, stack.EntryPoint)) diff --git a/api/http/handler/stacks/stack_inspect.go b/api/http/handler/stacks/stack_inspect.go index eb41dc793..3e78f66ec 100644 --- a/api/http/handler/stacks/stack_inspect.go +++ b/api/http/handler/stacks/stack_inspect.go @@ -7,6 +7,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,14 +20,14 @@ func (handler *Handler) stackInspect(w http.ResponseWriter, r *http.Request) *ht } stack, err := handler.DataStore.Stack().Stack(portainer.StackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} } endpoint, err := handler.DataStore.Endpoint().Endpoint(stack.EndpointID) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -51,7 +53,7 @@ func (handler *Handler) stackInspect(w http.ResponseWriter, r *http.Request) *ht return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify user authorizations to validate stack access", err} } if !access { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", errors.ErrResourceAccessDenied} } if resourceControl != nil { diff --git a/api/http/handler/stacks/stack_list.go b/api/http/handler/stacks/stack_list.go index 047740397..aff22b9a0 100644 --- a/api/http/handler/stacks/stack_list.go +++ b/api/http/handler/stacks/stack_list.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" ) @@ -45,9 +46,9 @@ func (handler *Handler) stackList(w http.ResponseWriter, r *http.Request) *httpe if !securityContext.IsAdmin { rbacExtensionEnabled := true _, err := handler.DataStore.Extension().Extension(portainer.RBACExtension) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { rbacExtensionEnabled = false - } else if err != nil && err != portainer.ErrObjectNotFound { + } else if err != nil && err != errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to check if RBAC extension is enabled", err} } diff --git a/api/http/handler/stacks/stack_migrate.go b/api/http/handler/stacks/stack_migrate.go index 1c0f98497..4e52c0e77 100644 --- a/api/http/handler/stacks/stack_migrate.go +++ b/api/http/handler/stacks/stack_migrate.go @@ -1,12 +1,15 @@ package stacks import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,7 +21,7 @@ type stackMigratePayload struct { func (payload *stackMigratePayload) Validate(r *http.Request) error { if payload.EndpointID == 0 { - return portainer.Error("Invalid endpoint identifier. Must be a positive number") + return errors.New("Invalid endpoint identifier. Must be a positive number") } return nil } @@ -37,14 +40,14 @@ func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *ht } stack, err := handler.DataStore.Stack().Stack(portainer.StackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} } endpoint, err := handler.DataStore.Endpoint().Endpoint(stack.EndpointID) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -70,7 +73,7 @@ func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *ht return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify user authorizations to validate stack access", err} } if !access { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", httperrors.ErrResourceAccessDenied} } // TODO: this is a work-around for stacks created with Portainer version >= 1.17.1 @@ -85,7 +88,7 @@ func (handler *Handler) stackMigrate(w http.ResponseWriter, r *http.Request) *ht } targetEndpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(payload.EndpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} diff --git a/api/http/handler/stacks/stack_update.go b/api/http/handler/stacks/stack_update.go index 3781f9a78..86a1e65e5 100644 --- a/api/http/handler/stacks/stack_update.go +++ b/api/http/handler/stacks/stack_update.go @@ -1,16 +1,18 @@ package stacks import ( + "errors" "net/http" "strconv" - "github.com/portainer/portainer/api/http/security" - "github.com/asaskevich/govalidator" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" + "github.com/portainer/portainer/api/http/security" ) type updateComposeStackPayload struct { @@ -20,7 +22,7 @@ type updateComposeStackPayload struct { func (payload *updateComposeStackPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } return nil } @@ -33,7 +35,7 @@ type updateSwarmStackPayload struct { func (payload *updateSwarmStackPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.StackFileContent) { - return portainer.Error("Invalid stack file content") + return errors.New("Invalid stack file content") } return nil } @@ -46,7 +48,7 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt } stack, err := handler.DataStore.Stack().Stack(portainer.StackID(stackID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a stack with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a stack with the specified identifier inside the database", err} @@ -64,7 +66,7 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt } endpoint, err := handler.DataStore.Endpoint().Endpoint(stack.EndpointID) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} @@ -90,7 +92,7 @@ func (handler *Handler) stackUpdate(w http.ResponseWriter, r *http.Request) *htt return &httperror.HandlerError{http.StatusInternalServerError, "Unable to verify user authorizations to validate stack access", err} } if !access { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to resource", httperrors.ErrResourceAccessDenied} } updateError := handler.updateAndDeployStack(r, stack, endpoint) diff --git a/api/http/handler/tags/tag_create.go b/api/http/handler/tags/tag_create.go index d1857bcf5..5a2d1e400 100644 --- a/api/http/handler/tags/tag_create.go +++ b/api/http/handler/tags/tag_create.go @@ -1,6 +1,7 @@ package tags import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -16,7 +17,7 @@ type tagCreatePayload struct { func (payload *tagCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid tag name") + return errors.New("Invalid tag name") } return nil } @@ -36,7 +37,7 @@ func (handler *Handler) tagCreate(w http.ResponseWriter, r *http.Request) *httpe for _, tag := range tags { if tag.Name == payload.Name { - return &httperror.HandlerError{http.StatusConflict, "This name is already associated to a tag", portainer.ErrTagAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "This name is already associated to a tag", errors.New("A tag already exists with this name")} } } diff --git a/api/http/handler/tags/tag_delete.go b/api/http/handler/tags/tag_delete.go index 5df4f2e2b..287af24bd 100644 --- a/api/http/handler/tags/tag_delete.go +++ b/api/http/handler/tags/tag_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/internal/edge" ) @@ -19,7 +20,7 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe tagID := portainer.TagID(id) tag, err := handler.DataStore.Tag().Tag(tagID) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a tag with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a tag with the specified identifier inside the database", err} diff --git a/api/http/handler/teammemberships/teammembership_create.go b/api/http/handler/teammemberships/teammembership_create.go index 21f5cd74d..506608522 100644 --- a/api/http/handler/teammemberships/teammembership_create.go +++ b/api/http/handler/teammemberships/teammembership_create.go @@ -1,12 +1,14 @@ package teammemberships import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,13 +20,13 @@ type teamMembershipCreatePayload struct { func (payload *teamMembershipCreatePayload) Validate(r *http.Request) error { if payload.UserID == 0 { - return portainer.Error("Invalid UserID") + return errors.New("Invalid UserID") } if payload.TeamID == 0 { - return portainer.Error("Invalid TeamID") + return errors.New("Invalid TeamID") } if payload.Role != 1 && payload.Role != 2 { - return portainer.Error("Invalid role value. Value must be one of: 1 (leader) or 2 (member)") + return errors.New("Invalid role value. Value must be one of: 1 (leader) or 2 (member)") } return nil } @@ -43,7 +45,7 @@ func (handler *Handler) teamMembershipCreate(w http.ResponseWriter, r *http.Requ } if !security.AuthorizedTeamManagement(portainer.TeamID(payload.TeamID), securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to manage team memberships", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to manage team memberships", httperrors.ErrResourceAccessDenied} } memberships, err := handler.DataStore.TeamMembership().TeamMembershipsByUserID(portainer.UserID(payload.UserID)) @@ -54,7 +56,7 @@ func (handler *Handler) teamMembershipCreate(w http.ResponseWriter, r *http.Requ if len(memberships) > 0 { for _, membership := range memberships { if membership.UserID == portainer.UserID(payload.UserID) && membership.TeamID == portainer.TeamID(payload.TeamID) { - return &httperror.HandlerError{http.StatusConflict, "Team membership already registered", portainer.ErrTeamMembershipAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "Team membership already registered", errors.New("Team membership already exists for this user and team")} } } } diff --git a/api/http/handler/teammemberships/teammembership_delete.go b/api/http/handler/teammemberships/teammembership_delete.go index 775ba4d1a..c12892325 100644 --- a/api/http/handler/teammemberships/teammembership_delete.go +++ b/api/http/handler/teammemberships/teammembership_delete.go @@ -7,6 +7,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,7 +20,7 @@ func (handler *Handler) teamMembershipDelete(w http.ResponseWriter, r *http.Requ } membership, err := handler.DataStore.TeamMembership().TeamMembership(portainer.TeamMembershipID(membershipID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a team membership with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a team membership with the specified identifier inside the database", err} @@ -30,7 +32,7 @@ func (handler *Handler) teamMembershipDelete(w http.ResponseWriter, r *http.Requ } if !security.AuthorizedTeamManagement(membership.TeamID, securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to delete the membership", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to delete the membership", errors.ErrResourceAccessDenied} } err = handler.DataStore.TeamMembership().DeleteTeamMembership(portainer.TeamMembershipID(membershipID)) diff --git a/api/http/handler/teammemberships/teammembership_list.go b/api/http/handler/teammemberships/teammembership_list.go index 20e42b599..77a113776 100644 --- a/api/http/handler/teammemberships/teammembership_list.go +++ b/api/http/handler/teammemberships/teammembership_list.go @@ -5,7 +5,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/response" - "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -17,7 +17,7 @@ func (handler *Handler) teamMembershipList(w http.ResponseWriter, r *http.Reques } if !securityContext.IsAdmin && !securityContext.IsTeamLeader { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to list team memberships", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to list team memberships", errors.ErrResourceAccessDenied} } memberships, err := handler.DataStore.TeamMembership().TeamMemberships() diff --git a/api/http/handler/teammemberships/teammembership_update.go b/api/http/handler/teammemberships/teammembership_update.go index d98929a75..cf801a65d 100644 --- a/api/http/handler/teammemberships/teammembership_update.go +++ b/api/http/handler/teammemberships/teammembership_update.go @@ -1,12 +1,15 @@ package teammemberships import ( + "errors" "net/http" httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,13 +21,13 @@ type teamMembershipUpdatePayload struct { func (payload *teamMembershipUpdatePayload) Validate(r *http.Request) error { if payload.UserID == 0 { - return portainer.Error("Invalid UserID") + return errors.New("Invalid UserID") } if payload.TeamID == 0 { - return portainer.Error("Invalid TeamID") + return errors.New("Invalid TeamID") } if payload.Role != 1 && payload.Role != 2 { - return portainer.Error("Invalid role value. Value must be one of: 1 (leader) or 2 (member)") + return errors.New("Invalid role value. Value must be one of: 1 (leader) or 2 (member)") } return nil } @@ -48,18 +51,18 @@ func (handler *Handler) teamMembershipUpdate(w http.ResponseWriter, r *http.Requ } if !security.AuthorizedTeamManagement(portainer.TeamID(payload.TeamID), securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the membership", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the membership", httperrors.ErrResourceAccessDenied} } membership, err := handler.DataStore.TeamMembership().TeamMembership(portainer.TeamMembershipID(membershipID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a team membership with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a team membership with the specified identifier inside the database", err} } if securityContext.IsTeamLeader && membership.Role != portainer.MembershipRole(payload.Role) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the role of membership", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update the role of membership", httperrors.ErrResourceAccessDenied} } membership.UserID = portainer.UserID(payload.UserID) diff --git a/api/http/handler/teams/team_create.go b/api/http/handler/teams/team_create.go index 9012b0b88..583087733 100644 --- a/api/http/handler/teams/team_create.go +++ b/api/http/handler/teams/team_create.go @@ -1,6 +1,7 @@ package teams import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type teamCreatePayload struct { @@ -16,7 +18,7 @@ type teamCreatePayload struct { func (payload *teamCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { - return portainer.Error("Invalid team name") + return errors.New("Invalid team name") } return nil } @@ -29,11 +31,11 @@ func (handler *Handler) teamCreate(w http.ResponseWriter, r *http.Request) *http } team, err := handler.DataStore.Team().TeamByName(payload.Name) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve teams from the database", err} } if team != nil { - return &httperror.HandlerError{http.StatusConflict, "A team with the same name already exists", portainer.ErrTeamAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A team with the same name already exists", errors.New("Team already exists")} } team = &portainer.Team{ diff --git a/api/http/handler/teams/team_delete.go b/api/http/handler/teams/team_delete.go index d1629347e..6d3ac534c 100644 --- a/api/http/handler/teams/team_delete.go +++ b/api/http/handler/teams/team_delete.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // DELETE request on /api/teams/:id @@ -17,7 +18,7 @@ func (handler *Handler) teamDelete(w http.ResponseWriter, r *http.Request) *http } _, err = handler.DataStore.Team().Team(portainer.TeamID(teamID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a team with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a team with the specified identifier inside the database", err} diff --git a/api/http/handler/teams/team_inspect.go b/api/http/handler/teams/team_inspect.go index f543a8f37..81d739824 100644 --- a/api/http/handler/teams/team_inspect.go +++ b/api/http/handler/teams/team_inspect.go @@ -7,6 +7,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -23,11 +25,11 @@ func (handler *Handler) teamInspect(w http.ResponseWriter, r *http.Request) *htt } if !security.AuthorizedTeamManagement(portainer.TeamID(teamID), securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to team", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to team", errors.ErrResourceAccessDenied} } team, err := handler.DataStore.Team().Team(portainer.TeamID(teamID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a team with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a team with the specified identifier inside the database", err} diff --git a/api/http/handler/teams/team_memberships.go b/api/http/handler/teams/team_memberships.go index 1d61e1a01..75c6f1389 100644 --- a/api/http/handler/teams/team_memberships.go +++ b/api/http/handler/teams/team_memberships.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -23,7 +24,7 @@ func (handler *Handler) teamMemberships(w http.ResponseWriter, r *http.Request) } if !security.AuthorizedTeamManagement(portainer.TeamID(teamID), securityContext) { - return &httperror.HandlerError{http.StatusForbidden, "Access denied to team", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Access denied to team", errors.ErrResourceAccessDenied} } memberships, err := handler.DataStore.TeamMembership().TeamMembershipsByTeamID(portainer.TeamID(teamID)) diff --git a/api/http/handler/teams/team_update.go b/api/http/handler/teams/team_update.go index 06e1ef9d0..eba25b11d 100644 --- a/api/http/handler/teams/team_update.go +++ b/api/http/handler/teams/team_update.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) type teamUpdatePayload struct { @@ -31,7 +32,7 @@ func (handler *Handler) teamUpdate(w http.ResponseWriter, r *http.Request) *http } team, err := handler.DataStore.Team().Team(portainer.TeamID(teamID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a team with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a team with the specified identifier inside the database", err} diff --git a/api/http/handler/upload/upload_tls.go b/api/http/handler/upload/upload_tls.go index 383f21fac..aa16544fb 100644 --- a/api/http/handler/upload/upload_tls.go +++ b/api/http/handler/upload/upload_tls.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" ) // POST request on /api/upload/tls/{certificate:(?:ca|cert|key)}?folder= @@ -35,7 +36,7 @@ func (handler *Handler) uploadTLS(w http.ResponseWriter, r *http.Request) *httpe case "key": fileType = portainer.TLSFileKey default: - return &httperror.HandlerError{http.StatusBadRequest, "Invalid certificate route value. Value must be one of: ca, cert or key", portainer.ErrUndefinedTLSFileType} + return &httperror.HandlerError{http.StatusBadRequest, "Invalid certificate route value. Value must be one of: ca, cert or key", filesystem.ErrUndefinedTLSFileType} } _, err = handler.FileService.StoreTLSFileFromBytes(folder, fileType, file) diff --git a/api/http/handler/users/admin_check.go b/api/http/handler/users/admin_check.go index 7c1a54f33..0bf503f7c 100644 --- a/api/http/handler/users/admin_check.go +++ b/api/http/handler/users/admin_check.go @@ -6,6 +6,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // GET request on /api/users/admin/check @@ -16,7 +17,7 @@ func (handler *Handler) adminCheck(w http.ResponseWriter, r *http.Request) *http } if len(users) == 0 { - return &httperror.HandlerError{http.StatusNotFound, "No administrator account found inside the database", portainer.ErrObjectNotFound} + return &httperror.HandlerError{http.StatusNotFound, "No administrator account found inside the database", errors.ErrObjectNotFound} } return response.Empty(w) diff --git a/api/http/handler/users/admin_init.go b/api/http/handler/users/admin_init.go index b76299199..d15ed7d7a 100644 --- a/api/http/handler/users/admin_init.go +++ b/api/http/handler/users/admin_init.go @@ -1,6 +1,7 @@ package users import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -18,10 +19,10 @@ type adminInitPayload struct { func (payload *adminInitPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Username) || govalidator.Contains(payload.Username, " ") { - return portainer.Error("Invalid username. Must not contain any whitespace") + return errors.New("Invalid username. Must not contain any whitespace") } if govalidator.IsNull(payload.Password) { - return portainer.Error("Invalid password") + return errors.New("Invalid password") } return nil } @@ -40,7 +41,7 @@ func (handler *Handler) adminInit(w http.ResponseWriter, r *http.Request) *httpe } if len(users) != 0 { - return &httperror.HandlerError{http.StatusConflict, "Unable to create administrator user", portainer.ErrAdminAlreadyInitialized} + return &httperror.HandlerError{http.StatusConflict, "Unable to create administrator user", errAdminAlreadyInitialized} } user := &portainer.User{ @@ -51,7 +52,7 @@ func (handler *Handler) adminInit(w http.ResponseWriter, r *http.Request) *httpe user.Password, err = handler.CryptoService.Hash(payload.Password) if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", portainer.ErrCryptoHashFailure} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", errCryptoHashFailure} } err = handler.DataStore.User().CreateUser(user) diff --git a/api/http/handler/users/handler.go b/api/http/handler/users/handler.go index 3413d4bb9..8c4da1c4a 100644 --- a/api/http/handler/users/handler.go +++ b/api/http/handler/users/handler.go @@ -1,6 +1,8 @@ package users import ( + "errors" + httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/security" @@ -11,6 +13,14 @@ import ( "github.com/gorilla/mux" ) +var ( + errUserAlreadyExists = errors.New("User already exists") + errAdminAlreadyInitialized = errors.New("An administrator user already exists") + errAdminCannotRemoveSelf = errors.New("Cannot remove your own user account. Contact another administrator") + errCannotRemoveLastLocalAdmin = errors.New("Cannot remove the last local administrator account") + errCryptoHashFailure = errors.New("Unable to hash data") +) + func hideFields(user *portainer.User) { user.Password = "" } diff --git a/api/http/handler/users/user_create.go b/api/http/handler/users/user_create.go index d39d1a9a7..36f9d9039 100644 --- a/api/http/handler/users/user_create.go +++ b/api/http/handler/users/user_create.go @@ -1,6 +1,7 @@ package users import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" ) @@ -20,11 +23,11 @@ type userCreatePayload struct { func (payload *userCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Username) || govalidator.Contains(payload.Username, " ") { - return portainer.Error("Invalid username. Must not contain any whitespace") + return errors.New("Invalid username. Must not contain any whitespace") } if payload.Role != 1 && payload.Role != 2 { - return portainer.Error("Invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)") + return errors.New("Invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)") } return nil } @@ -43,19 +46,19 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http } if !securityContext.IsAdmin && !securityContext.IsTeamLeader { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to create user", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to create user", httperrors.ErrResourceAccessDenied} } if securityContext.IsTeamLeader && payload.Role == 1 { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to create administrator user", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to create administrator user", httperrors.ErrResourceAccessDenied} } user, err := handler.DataStore.User().UserByUsername(payload.Username) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve users from the database", err} } if user != nil { - return &httperror.HandlerError{http.StatusConflict, "Another user with the same username already exists", portainer.ErrUserAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "Another user with the same username already exists", errUserAlreadyExists} } user = &portainer.User{ @@ -72,7 +75,7 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http if settings.AuthenticationMethod == portainer.AuthenticationInternal { user.Password, err = handler.CryptoService.Hash(payload.Password) if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", portainer.ErrCryptoHashFailure} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", errCryptoHashFailure} } } diff --git a/api/http/handler/users/user_delete.go b/api/http/handler/users/user_delete.go index e192cd055..12a1be2c0 100644 --- a/api/http/handler/users/user_delete.go +++ b/api/http/handler/users/user_delete.go @@ -8,6 +8,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/http/security" ) @@ -28,11 +29,11 @@ func (handler *Handler) userDelete(w http.ResponseWriter, r *http.Request) *http } if tokenData.ID == portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusForbidden, "Cannot remove your own user account. Contact another administrator", portainer.ErrAdminCannotRemoveSelf} + return &httperror.HandlerError{http.StatusForbidden, "Cannot remove your own user account. Contact another administrator", errAdminCannotRemoveSelf} } user, err := handler.DataStore.User().User(portainer.UserID(userID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a user with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a user with the specified identifier inside the database", err} @@ -63,7 +64,7 @@ func (handler *Handler) deleteAdminUser(w http.ResponseWriter, user *portainer.U } if localAdminCount < 2 { - return &httperror.HandlerError{http.StatusInternalServerError, "Cannot remove local administrator user", portainer.ErrCannotRemoveLastLocalAdmin} + return &httperror.HandlerError{http.StatusInternalServerError, "Cannot remove local administrator user", errCannotRemoveLastLocalAdmin} } return handler.deleteUser(w, user) diff --git a/api/http/handler/users/user_inspect.go b/api/http/handler/users/user_inspect.go index b35d67d3a..cfd87efd0 100644 --- a/api/http/handler/users/user_inspect.go +++ b/api/http/handler/users/user_inspect.go @@ -3,12 +3,13 @@ package users import ( "net/http" - "github.com/portainer/portainer/api/http/security" - httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + "github.com/portainer/portainer/api/http/errors" + "github.com/portainer/portainer/api/http/security" ) // GET request on /api/users/:id @@ -24,11 +25,11 @@ func (handler *Handler) userInspect(w http.ResponseWriter, r *http.Request) *htt } if !securityContext.IsAdmin && securityContext.UserID != portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied inspect user", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied inspect user", errors.ErrResourceAccessDenied} } user, err := handler.DataStore.User().User(portainer.UserID(userID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a user with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a user with the specified identifier inside the database", err} diff --git a/api/http/handler/users/user_memberships.go b/api/http/handler/users/user_memberships.go index 3d4655bbb..283b6ee25 100644 --- a/api/http/handler/users/user_memberships.go +++ b/api/http/handler/users/user_memberships.go @@ -7,6 +7,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -23,7 +24,7 @@ func (handler *Handler) userMemberships(w http.ResponseWriter, r *http.Request) } if tokenData.Role != portainer.AdministratorRole && tokenData.ID != portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user memberships", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user memberships", errors.ErrUnauthorized} } memberships, err := handler.DataStore.TeamMembership().TeamMembershipsByUserID(portainer.UserID(userID)) diff --git a/api/http/handler/users/user_update.go b/api/http/handler/users/user_update.go index 55e2f4e59..dd1f57519 100644 --- a/api/http/handler/users/user_update.go +++ b/api/http/handler/users/user_update.go @@ -1,6 +1,7 @@ package users import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -19,11 +22,11 @@ type userUpdatePayload struct { func (payload *userUpdatePayload) Validate(r *http.Request) error { if govalidator.Contains(payload.Username, " ") { - return portainer.Error("Invalid username. Must not contain any whitespace") + return errors.New("Invalid username. Must not contain any whitespace") } if payload.Role != 0 && payload.Role != 1 && payload.Role != 2 { - return portainer.Error("Invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)") + return errors.New("Invalid role value. Value must be one of: 1 (administrator) or 2 (regular user)") } return nil } @@ -41,7 +44,7 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http } if tokenData.Role != portainer.AdministratorRole && tokenData.ID != portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user", httperrors.ErrUnauthorized} } var payload userUpdatePayload @@ -51,11 +54,11 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http } if tokenData.Role != portainer.AdministratorRole && payload.Role != 0 { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user to administrator role", portainer.ErrResourceAccessDenied} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user to administrator role", httperrors.ErrResourceAccessDenied} } user, err := handler.DataStore.User().User(portainer.UserID(userID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a user with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a user with the specified identifier inside the database", err} @@ -63,11 +66,11 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http if payload.Username != "" && payload.Username != user.Username { sameNameUser, err := handler.DataStore.User().UserByUsername(payload.Username) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve users from the database", err} } if sameNameUser != nil && sameNameUser.ID != portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusConflict, "Another user with the same username already exists", portainer.ErrUserAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "Another user with the same username already exists", errUserAlreadyExists} } user.Username = payload.Username @@ -76,7 +79,7 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http if payload.Password != "" { user.Password, err = handler.CryptoService.Hash(payload.Password) if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", portainer.ErrCryptoHashFailure} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", errCryptoHashFailure} } } diff --git a/api/http/handler/users/user_update_password.go b/api/http/handler/users/user_update_password.go index 8905a0462..c0556dfd1 100644 --- a/api/http/handler/users/user_update_password.go +++ b/api/http/handler/users/user_update_password.go @@ -1,6 +1,7 @@ package users import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -8,6 +9,8 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" "github.com/portainer/portainer/api/http/security" ) @@ -18,10 +21,10 @@ type userUpdatePasswordPayload struct { func (payload *userUpdatePasswordPayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Password) { - return portainer.Error("Invalid current password") + return errors.New("Invalid current password") } if govalidator.IsNull(payload.NewPassword) { - return portainer.Error("Invalid new password") + return errors.New("Invalid new password") } return nil } @@ -39,7 +42,7 @@ func (handler *Handler) userUpdatePassword(w http.ResponseWriter, r *http.Reques } if tokenData.Role != portainer.AdministratorRole && tokenData.ID != portainer.UserID(userID) { - return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Permission denied to update user", httperrors.ErrUnauthorized} } var payload userUpdatePasswordPayload @@ -49,7 +52,7 @@ func (handler *Handler) userUpdatePassword(w http.ResponseWriter, r *http.Reques } user, err := handler.DataStore.User().User(portainer.UserID(userID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a user with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a user with the specified identifier inside the database", err} @@ -57,12 +60,12 @@ func (handler *Handler) userUpdatePassword(w http.ResponseWriter, r *http.Reques err = handler.CryptoService.CompareHashAndData(user.Password, payload.Password) if err != nil { - return &httperror.HandlerError{http.StatusForbidden, "Specified password do not match actual password", portainer.ErrUnauthorized} + return &httperror.HandlerError{http.StatusForbidden, "Specified password do not match actual password", httperrors.ErrUnauthorized} } user.Password, err = handler.CryptoService.Hash(payload.NewPassword) if err != nil { - return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", portainer.ErrCryptoHashFailure} + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to hash user password", errCryptoHashFailure} } err = handler.DataStore.User().UpdateUser(user.ID, user) diff --git a/api/http/handler/webhooks/webhook_create.go b/api/http/handler/webhooks/webhook_create.go index 5b960ec2d..dc00dcd38 100644 --- a/api/http/handler/webhooks/webhook_create.go +++ b/api/http/handler/webhooks/webhook_create.go @@ -1,6 +1,7 @@ package webhooks import ( + "errors" "net/http" "github.com/asaskevich/govalidator" @@ -9,6 +10,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) type webhookCreatePayload struct { @@ -19,13 +21,13 @@ type webhookCreatePayload struct { func (payload *webhookCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.ResourceID) { - return portainer.Error("Invalid ResourceID") + return errors.New("Invalid ResourceID") } if payload.EndpointID == 0 { - return portainer.Error("Invalid EndpointID") + return errors.New("Invalid EndpointID") } if payload.WebhookType != 1 { - return portainer.Error("Invalid WebhookType") + return errors.New("Invalid WebhookType") } return nil } @@ -38,11 +40,11 @@ func (handler *Handler) webhookCreate(w http.ResponseWriter, r *http.Request) *h } webhook, err := handler.DataStore.Webhook().WebhookByResourceID(payload.ResourceID) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusInternalServerError, "An error occurred retrieving webhooks from the database", err} } if webhook != nil { - return &httperror.HandlerError{http.StatusConflict, "A webhook for this resource already exists", portainer.ErrWebhookAlreadyExists} + return &httperror.HandlerError{http.StatusConflict, "A webhook for this resource already exists", errors.New("A webhook for this resource already exists")} } token, err := uuid.NewV4() diff --git a/api/http/handler/webhooks/webhook_execute.go b/api/http/handler/webhooks/webhook_execute.go index 11e62fd99..e07ae8b2e 100644 --- a/api/http/handler/webhooks/webhook_execute.go +++ b/api/http/handler/webhooks/webhook_execute.go @@ -2,6 +2,7 @@ package webhooks import ( "context" + "errors" "net/http" "strings" @@ -10,6 +11,7 @@ import ( "github.com/portainer/libhttp/request" "github.com/portainer/libhttp/response" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) // Acts on a passed in token UUID to restart the docker service @@ -23,7 +25,7 @@ func (handler *Handler) webhookExecute(w http.ResponseWriter, r *http.Request) * webhook, err := handler.DataStore.Webhook().WebhookByToken(webhookToken) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find a webhook with this token", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve webhook from the database", err} @@ -34,7 +36,7 @@ func (handler *Handler) webhookExecute(w http.ResponseWriter, r *http.Request) * webhookType := webhook.WebhookType endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} @@ -46,7 +48,7 @@ func (handler *Handler) webhookExecute(w http.ResponseWriter, r *http.Request) * case portainer.ServiceWebhook: return handler.executeServiceWebhook(w, endpoint, resourceID, imageTag) default: - return &httperror.HandlerError{http.StatusInternalServerError, "Unsupported webhook type", portainer.ErrUnsupportedWebhookType} + return &httperror.HandlerError{http.StatusInternalServerError, "Unsupported webhook type", errors.New("Webhooks for this resource are not currently supported")} } } diff --git a/api/http/handler/websocket/attach.go b/api/http/handler/websocket/attach.go index dee223853..6da16659a 100644 --- a/api/http/handler/websocket/attach.go +++ b/api/http/handler/websocket/attach.go @@ -11,6 +11,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/bolt/errors" ) // websocketAttach handles GET requests on /websocket/attach?id=&endpointId=&nodeName=&token= @@ -33,7 +34,7 @@ func (handler *Handler) websocketAttach(w http.ResponseWriter, r *http.Request) } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} diff --git a/api/http/handler/websocket/exec.go b/api/http/handler/websocket/exec.go index eb59956ff..0abbcb263 100644 --- a/api/http/handler/websocket/exec.go +++ b/api/http/handler/websocket/exec.go @@ -3,6 +3,7 @@ package websocket import ( "bytes" "encoding/json" + "github.com/portainer/portainer/api/bolt/errors" "net" "net/http" "net/http/httputil" @@ -40,7 +41,7 @@ func (handler *Handler) websocketExec(w http.ResponseWriter, r *http.Request) *h } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == errors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} diff --git a/api/http/handler/websocket/pod.go b/api/http/handler/websocket/pod.go index 46f7f1dfd..103b2b60f 100644 --- a/api/http/handler/websocket/pod.go +++ b/api/http/handler/websocket/pod.go @@ -10,6 +10,7 @@ import ( httperror "github.com/portainer/libhttp/error" "github.com/portainer/libhttp/request" portainer "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" ) // websocketPodExec handles GET requests on /websocket/pod?token=&endpointId=&namespace=&podName=&containerName=&command= @@ -49,7 +50,7 @@ func (handler *Handler) websocketPodExec(w http.ResponseWriter, r *http.Request) } endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return &httperror.HandlerError{http.StatusNotFound, "Unable to find the endpoint associated to the stack inside the database", err} } else if err != nil { return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find the endpoint associated to the stack inside the database", err} diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index cf9b64a86..5be06eb0b 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -12,6 +12,7 @@ import ( "github.com/docker/docker/client" "github.com/portainer/portainer/api" + bolterrors "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/docker" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" "github.com/portainer/portainer/api/http/security" @@ -402,7 +403,7 @@ func (transport *Transport) restrictedResourceOperation(request *http.Request, r if tokenData.Role != portainer.AdministratorRole { rbacExtension, err := transport.dataStore.Extension().Extension(portainer.RBACExtension) - if err != nil && err != portainer.ErrObjectNotFound { + if err != nil && err != bolterrors.ErrObjectNotFound { return nil, err } diff --git a/api/http/security/bouncer.go b/api/http/security/bouncer.go index 97abab731..ca8c165cb 100644 --- a/api/http/security/bouncer.go +++ b/api/http/security/bouncer.go @@ -2,12 +2,13 @@ package security import ( "errors" + "net/http" + "strings" httperror "github.com/portainer/libhttp/error" "github.com/portainer/portainer/api" - - "net/http" - "strings" + bolterrors "github.com/portainer/portainer/api/bolt/errors" + httperrors "github.com/portainer/portainer/api/http/errors" ) type ( @@ -110,13 +111,13 @@ func (bouncer *RequestBouncer) AuthorizedEndpointOperation(r *http.Request, endp } if !authorizedEndpointAccess(endpoint, group, tokenData.ID, memberships) { - return portainer.ErrEndpointAccessDenied + return httperrors.ErrEndpointAccessDenied } if authorizationCheck { err = bouncer.checkEndpointOperationAuthorization(r, endpoint) if err != nil { - return portainer.ErrAuthorizationRequired + return ErrAuthorizationRequired } } @@ -152,7 +153,7 @@ func (bouncer *RequestBouncer) checkEndpointOperationAuthorization(r *http.Reque } extension, err := bouncer.dataStore.Extension().Extension(portainer.RBACExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { return nil } else if err != nil { return err @@ -192,7 +193,7 @@ func (bouncer *RequestBouncer) RegistryAccess(r *http.Request, registry *portain } if !AuthorizedRegistryAccess(registry, tokenData.ID, memberships) { - return portainer.ErrEndpointAccessDenied + return httperrors.ErrEndpointAccessDenied } return nil @@ -213,7 +214,7 @@ func (bouncer *RequestBouncer) mwCheckPortainerAuthorizations(next http.Handler, return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tokenData, err := RetrieveTokenData(r) if err != nil { - httperror.WriteError(w, http.StatusForbidden, "Access denied", portainer.ErrUnauthorized) + httperror.WriteError(w, http.StatusForbidden, "Access denied", httperrors.ErrUnauthorized) return } @@ -223,9 +224,9 @@ func (bouncer *RequestBouncer) mwCheckPortainerAuthorizations(next http.Handler, } extension, err := bouncer.dataStore.Extension().Extension(portainer.RBACExtension) - if err == portainer.ErrObjectNotFound { + if err == bolterrors.ErrObjectNotFound { if administratorOnly { - httperror.WriteError(w, http.StatusForbidden, "Access denied", portainer.ErrUnauthorized) + httperror.WriteError(w, http.StatusForbidden, "Access denied", httperrors.ErrUnauthorized) return } @@ -237,8 +238,8 @@ func (bouncer *RequestBouncer) mwCheckPortainerAuthorizations(next http.Handler, } user, err := bouncer.dataStore.User().User(tokenData.ID) - if err != nil && err == portainer.ErrObjectNotFound { - httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", portainer.ErrUnauthorized) + if err != nil && err == bolterrors.ErrObjectNotFound { + httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", httperrors.ErrUnauthorized) return } else if err != nil { httperror.WriteError(w, http.StatusInternalServerError, "Unable to retrieve user details from the database", err) @@ -254,7 +255,7 @@ func (bouncer *RequestBouncer) mwCheckPortainerAuthorizations(next http.Handler, bouncer.rbacExtensionClient.setLicenseKey(extension.License.LicenseKey) err = bouncer.rbacExtensionClient.checkAuthorization(apiOperation) if err != nil { - httperror.WriteError(w, http.StatusForbidden, "Access denied", portainer.ErrAuthorizationRequired) + httperror.WriteError(w, http.StatusForbidden, "Access denied", ErrAuthorizationRequired) return } @@ -268,7 +269,7 @@ func (bouncer *RequestBouncer) mwUpgradeToRestrictedRequest(next http.Handler) h return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { tokenData, err := RetrieveTokenData(r) if err != nil { - httperror.WriteError(w, http.StatusForbidden, "Access denied", portainer.ErrResourceAccessDenied) + httperror.WriteError(w, http.StatusForbidden, "Access denied", httperrors.ErrResourceAccessDenied) return } @@ -301,7 +302,7 @@ func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Han } if token == "" { - httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", portainer.ErrUnauthorized) + httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", httperrors.ErrUnauthorized) return } @@ -313,8 +314,8 @@ func (bouncer *RequestBouncer) mwCheckAuthentication(next http.Handler) http.Han } _, err = bouncer.dataStore.User().User(tokenData.ID) - if err != nil && err == portainer.ErrObjectNotFound { - httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", portainer.ErrUnauthorized) + if err != nil && err == bolterrors.ErrObjectNotFound { + httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", httperrors.ErrUnauthorized) return } else if err != nil { httperror.WriteError(w, http.StatusInternalServerError, "Unable to retrieve user details from the database", err) diff --git a/api/http/security/context.go b/api/http/security/context.go index 58daaa9e3..8350fa56f 100644 --- a/api/http/security/context.go +++ b/api/http/security/context.go @@ -2,6 +2,7 @@ package security import ( "context" + "errors" "net/http" "github.com/portainer/portainer/api" @@ -25,7 +26,7 @@ func storeTokenData(request *http.Request, tokenData *portainer.TokenData) conte func RetrieveTokenData(request *http.Request) (*portainer.TokenData, error) { contextData := request.Context().Value(contextAuthenticationKey) if contextData == nil { - return nil, portainer.ErrMissingContextData + return nil, errors.New("Unable to find JWT data in request context") } tokenData := contextData.(*portainer.TokenData) @@ -42,7 +43,7 @@ func storeRestrictedRequestContext(request *http.Request, requestContext *Restri func RetrieveRestrictedRequestContext(request *http.Request) (*RestrictedRequestContext, error) { contextData := request.Context().Value(contextRestrictedRequest) if contextData == nil { - return nil, portainer.ErrMissingSecurityContext + return nil, errors.New("Unable to find security details in request context") } requestContext := contextData.(*RestrictedRequestContext) diff --git a/api/http/security/errors.go b/api/http/security/errors.go new file mode 100644 index 000000000..40193b38c --- /dev/null +++ b/api/http/security/errors.go @@ -0,0 +1,7 @@ +package security + +import "errors" + +var ( + ErrAuthorizationRequired = errors.New("Authorization required for this operation") +) diff --git a/api/http/security/rate_limiter.go b/api/http/security/rate_limiter.go index e8cc7ae5c..65eea5d4a 100644 --- a/api/http/security/rate_limiter.go +++ b/api/http/security/rate_limiter.go @@ -7,7 +7,7 @@ import ( "github.com/g07cha/defender" httperror "github.com/portainer/libhttp/error" - "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/http/errors" ) // RateLimiter represents an entity that manages request rate limiting @@ -30,7 +30,7 @@ func (limiter *RateLimiter) LimitAccess(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ip := StripAddrPort(r.RemoteAddr) if banned := limiter.Inc(ip); banned == true { - httperror.WriteError(w, http.StatusForbidden, "Access denied", portainer.ErrResourceAccessDenied) + httperror.WriteError(w, http.StatusForbidden, "Access denied", errors.ErrResourceAccessDenied) return } next.ServeHTTP(w, r) diff --git a/api/http/security/rbac.go b/api/http/security/rbac.go index 08366cb72..f4281331b 100644 --- a/api/http/security/rbac.go +++ b/api/http/security/rbac.go @@ -52,7 +52,7 @@ func (client *rbacExtensionClient) checkAuthorization(authRequest *portainer.API defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent { - return portainer.ErrAuthorizationRequired + return ErrAuthorizationRequired } return nil diff --git a/api/jwt/jwt.go b/api/jwt/jwt.go index aedb264de..4bd9f8fec 100644 --- a/api/jwt/jwt.go +++ b/api/jwt/jwt.go @@ -1,6 +1,8 @@ package jwt import ( + "errors" + "github.com/portainer/portainer/api" "fmt" @@ -23,6 +25,11 @@ type claims struct { jwt.StandardClaims } +var ( + errSecretGeneration = errors.New("Unable to generate secret key") + errInvalidJWTToken = errors.New("Invalid JWT token") +) + // NewService initializes a new service. It will generate a random key that will be used to sign JWT tokens. func NewService(userSessionDuration string) (*Service, error) { userSessionTimeout, err := time.ParseDuration(userSessionDuration) @@ -32,7 +39,7 @@ func NewService(userSessionDuration string) (*Service, error) { secret := securecookie.GenerateRandomKey(32) if secret == nil { - return nil, portainer.ErrSecretGeneration + return nil, errSecretGeneration } service := &Service{ @@ -83,7 +90,7 @@ func (service *Service) ParseAndVerifyToken(token string) (*portainer.TokenData, } } - return nil, portainer.ErrInvalidJWTToken + return nil, errInvalidJWTToken } // SetUserSessionDuration sets the user session duration diff --git a/api/ldap/ldap.go b/api/ldap/ldap.go index c35f3a422..89c08ec61 100644 --- a/api/ldap/ldap.go +++ b/api/ldap/ldap.go @@ -1,18 +1,20 @@ package ldap import ( + "errors" "fmt" "strings" ldap "github.com/go-ldap/ldap/v3" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/crypto" + httperrors "github.com/portainer/portainer/api/http/errors" ) -const ( - // ErrUserNotFound defines an error raised when the user is not found via LDAP search +var ( + // errUserNotFound defines an error raised when the user is not found via LDAP search // or that too many entries (> 1) are returned. - ErrUserNotFound = portainer.Error("User not found or too many entries returned") + errUserNotFound = errors.New("User not found or too many entries returned") ) // Service represents a service used to authenticate users against a LDAP/AD. @@ -47,7 +49,7 @@ func searchUser(username string, conn *ldap.Conn, settings []portainer.LDAPSearc } if !found { - return "", ErrUserNotFound + return "", errUserNotFound } return userDN, nil @@ -105,7 +107,7 @@ func (*Service) AuthenticateUser(username, password string, settings *portainer. err = connection.Bind(userDN, password) if err != nil { - return portainer.ErrUnauthorized + return httperrors.ErrUnauthorized } return nil