From 19fa40286aa33a62c76e827a209bdd3f50bfd203 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:42:16 -0300 Subject: [PATCH] chore(fdo): remove FDO code EE-7235 (#11981) --- api/dataservices/fdoprofile/fdoprofile.go | 43 --- api/dataservices/interface.go | 7 - api/datastore/migrator/migrator.go | 4 - api/datastore/services.go | 13 - api/datastore/services_tx.go | 1 - .../test_data/output_24_to_latest.json | 6 - api/filesystem/filesystem.go | 19 -- api/hostmanagement/fdo/owner_client.go | 247 ------------------ api/http/handler/handler.go | 4 - .../handler/hostmanagement/fdo/configure.go | 201 -------------- api/http/handler/hostmanagement/fdo/fdo.go | 163 ------------ .../handler/hostmanagement/fdo/handler.go | 40 --- api/http/handler/hostmanagement/fdo/list.go | 41 --- .../hostmanagement/fdo/profile_create.go | 95 ------- .../hostmanagement/fdo/profile_delete.go | 37 --- .../hostmanagement/fdo/profile_duplicate.go | 66 ----- .../hostmanagement/fdo/profile_inspect.go | 50 ---- .../hostmanagement/fdo/profile_list.go | 31 --- .../hostmanagement/fdo/profile_update.go | 68 ----- .../handler/hostmanagement/fdo/register.go | 53 ---- api/http/handler/hostmanagement/fdo/utils.go | 16 -- api/http/handler/settings/settings_public.go | 3 - api/http/server.go | 4 - api/internal/testhelpers/datastore.go | 5 - api/portainer.go | 28 +- app/portainer/__module.js | 34 --- .../hostmanagement/fdo/fdo.service.ts | 99 ------- app/portainer/hostmanagement/fdo/model.ts | 20 -- app/portainer/models/settings.js | 2 - app/portainer/react/components/settings.ts | 5 - app/portainer/services/fileUpload.js | 10 - .../views/devices/import/importDevice.html | 237 ----------------- .../devices/import/importDeviceController.js | 138 ---------- .../devices/profiles/add/addProfile.html | 65 ----- .../profiles/add/addProfileController.js | 70 ----- .../views/devices/profiles/add/index.js | 8 - .../devices/profiles/edit/editProfile.html | 66 ----- .../profiles/edit/editProfileController.js | 88 ------- .../views/devices/profiles/edit/index.js | 8 - .../edge-compute/settingsEdgeCompute.html | 6 - .../settingsEdgeComputeController.js | 11 - .../ListView/EnvironmentsDatatable.tsx | 3 - .../ListView/ImportFdoDeviceButton.tsx | 32 --- .../portainer/feature-flags/useFeatureFlag.ts | 4 +- .../FDOProfilesDatatable.tsx | 45 ---- .../FDOProfilesDatatableActions.tsx | 108 -------- .../FDOProfilesDatatable/columns/created.tsx | 12 - .../FDOProfilesDatatable/columns/helper.ts | 5 - .../FDOProfilesDatatable/columns/index.tsx | 14 - .../FDOProfilesDatatable/index.ts | 1 - .../FDOProfilesDatatable/useFDOProfiles.tsx | 30 --- .../SettingsFDO/SettingsFDO.module.css | 3 - .../SettingsFDO/SettingsFDO.tsx | 212 --------------- .../SettingsFDO/SettingsFDO.validation.ts | 18 -- .../EdgeComputeView/SettingsFDO/index.ts | 1 - .../SettingsOpenAMT/SettingsOpenAMT.tsx | 2 +- app/react/portainer/settings/types.ts | 10 - 57 files changed, 3 insertions(+), 2609 deletions(-) delete mode 100644 api/dataservices/fdoprofile/fdoprofile.go delete mode 100644 api/hostmanagement/fdo/owner_client.go delete mode 100644 api/http/handler/hostmanagement/fdo/configure.go delete mode 100644 api/http/handler/hostmanagement/fdo/fdo.go delete mode 100644 api/http/handler/hostmanagement/fdo/handler.go delete mode 100644 api/http/handler/hostmanagement/fdo/list.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_create.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_delete.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_duplicate.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_inspect.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_list.go delete mode 100644 api/http/handler/hostmanagement/fdo/profile_update.go delete mode 100644 api/http/handler/hostmanagement/fdo/register.go delete mode 100644 api/http/handler/hostmanagement/fdo/utils.go delete mode 100644 app/portainer/hostmanagement/fdo/fdo.service.ts delete mode 100644 app/portainer/hostmanagement/fdo/model.ts delete mode 100644 app/portainer/views/devices/import/importDevice.html delete mode 100644 app/portainer/views/devices/import/importDeviceController.js delete mode 100644 app/portainer/views/devices/profiles/add/addProfile.html delete mode 100644 app/portainer/views/devices/profiles/add/addProfileController.js delete mode 100644 app/portainer/views/devices/profiles/add/index.js delete mode 100644 app/portainer/views/devices/profiles/edit/editProfile.html delete mode 100644 app/portainer/views/devices/profiles/edit/editProfileController.js delete mode 100644 app/portainer/views/devices/profiles/edit/index.js delete mode 100644 app/react/portainer/environments/ListView/ImportFdoDeviceButton.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatable.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/created.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/helper.ts delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/index.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/index.ts delete mode 100644 app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/useFDOProfiles.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.module.css delete mode 100644 app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.tsx delete mode 100644 app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.validation.ts delete mode 100644 app/react/portainer/settings/EdgeComputeView/SettingsFDO/index.ts diff --git a/api/dataservices/fdoprofile/fdoprofile.go b/api/dataservices/fdoprofile/fdoprofile.go deleted file mode 100644 index c5076d6ee..000000000 --- a/api/dataservices/fdoprofile/fdoprofile.go +++ /dev/null @@ -1,43 +0,0 @@ -package fdoprofile - -import ( - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/dataservices" -) - -// BucketName represents the name of the bucket where this service stores data. -const BucketName = "fdo_profiles" - -// Service represents a service for managingFDO Profiles data. -type Service struct { - dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID] -} - -// NewService creates a new instance of a service. -func NewService(connection portainer.Connection) (*Service, error) { - err := connection.SetServiceName(BucketName) - if err != nil { - return nil, err - } - - return &Service{ - BaseDataService: dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID]{ - Bucket: BucketName, - Connection: connection, - }, - }, nil -} - -// Create assign an ID to a new FDO Profile and saves it. -func (service *Service) Create(FDOProfile *portainer.FDOProfile) error { - return service.Connection.CreateObjectWithId( - BucketName, - int(FDOProfile.ID), - FDOProfile, - ) -} - -// GetNextIdentifier returns the next identifier for a FDO Profile. -func (service *Service) GetNextIdentifier() int { - return service.Connection.GetNextIdentifier(BucketName) -} diff --git a/api/dataservices/interface.go b/api/dataservices/interface.go index a20e48c66..1efef4f70 100644 --- a/api/dataservices/interface.go +++ b/api/dataservices/interface.go @@ -15,7 +15,6 @@ type ( Endpoint() EndpointService EndpointGroup() EndpointGroupService EndpointRelation() EndpointRelationService - FDOProfile() FDOProfileService HelmUserRepository() HelmUserRepositoryService Registry() RegistryService ResourceControl() ResourceControlService @@ -120,12 +119,6 @@ type ( BucketName() string } - // FDOProfileService represents a service to manage FDO Profiles - FDOProfileService interface { - BaseCRUD[portainer.FDOProfile, portainer.FDOProfileID] - GetNextIdentifier() int - } - // HelmUserRepositoryService represents a service to manage HelmUserRepositories HelmUserRepositoryService interface { BaseCRUD[portainer.HelmUserRepository, portainer.HelmUserRepositoryID] diff --git a/api/datastore/migrator/migrator.go b/api/datastore/migrator/migrator.go index 9347fd834..f4cfd91bf 100644 --- a/api/datastore/migrator/migrator.go +++ b/api/datastore/migrator/migrator.go @@ -13,7 +13,6 @@ import ( "github.com/portainer/portainer/api/dataservices/endpointgroup" "github.com/portainer/portainer/api/dataservices/endpointrelation" "github.com/portainer/portainer/api/dataservices/extension" - "github.com/portainer/portainer/api/dataservices/fdoprofile" "github.com/portainer/portainer/api/dataservices/pendingactions" "github.com/portainer/portainer/api/dataservices/registry" "github.com/portainer/portainer/api/dataservices/resourcecontrol" @@ -41,7 +40,6 @@ type ( endpointService *endpoint.Service endpointRelationService *endpointrelation.Service extensionService *extension.Service - fdoProfilesService *fdoprofile.Service registryService *registry.Service resourceControlService *resourcecontrol.Service roleService *role.Service @@ -69,7 +67,6 @@ type ( EndpointService *endpoint.Service EndpointRelationService *endpointrelation.Service ExtensionService *extension.Service - FDOProfilesService *fdoprofile.Service RegistryService *registry.Service ResourceControlService *resourcecontrol.Service RoleService *role.Service @@ -99,7 +96,6 @@ func NewMigrator(parameters *MigratorParameters) *Migrator { endpointService: parameters.EndpointService, endpointRelationService: parameters.EndpointRelationService, extensionService: parameters.ExtensionService, - fdoProfilesService: parameters.FDOProfilesService, registryService: parameters.RegistryService, resourceControlService: parameters.ResourceControlService, roleService: parameters.RoleService, diff --git a/api/datastore/services.go b/api/datastore/services.go index dfa590423..7d95ac709 100644 --- a/api/datastore/services.go +++ b/api/datastore/services.go @@ -17,7 +17,6 @@ import ( "github.com/portainer/portainer/api/dataservices/endpointgroup" "github.com/portainer/portainer/api/dataservices/endpointrelation" "github.com/portainer/portainer/api/dataservices/extension" - "github.com/portainer/portainer/api/dataservices/fdoprofile" "github.com/portainer/portainer/api/dataservices/helmuserrepository" "github.com/portainer/portainer/api/dataservices/pendingactions" "github.com/portainer/portainer/api/dataservices/registry" @@ -55,7 +54,6 @@ type Store struct { EndpointService *endpoint.Service EndpointRelationService *endpointrelation.Service ExtensionService *extension.Service - FDOProfilesService *fdoprofile.Service HelmUserRepositoryService *helmuserrepository.Service RegistryService *registry.Service ResourceControlService *resourcecontrol.Service @@ -138,12 +136,6 @@ func (store *Store) initServices() error { } store.ExtensionService = extensionService - fdoProfilesService, err := fdoprofile.NewService(store.connection) - if err != nil { - return err - } - store.FDOProfilesService = fdoProfilesService - helmUserRepositoryService, err := helmuserrepository.NewService(store.connection) if err != nil { return err @@ -289,11 +281,6 @@ func (store *Store) EndpointRelation() dataservices.EndpointRelationService { return store.EndpointRelationService } -// FDOProfile gives access to the FDOProfile data management layer -func (store *Store) FDOProfile() dataservices.FDOProfileService { - return store.FDOProfilesService -} - // HelmUserRepository access the helm user repository settings func (store *Store) HelmUserRepository() dataservices.HelmUserRepositoryService { return store.HelmUserRepositoryService diff --git a/api/datastore/services_tx.go b/api/datastore/services_tx.go index c20d632ba..367048322 100644 --- a/api/datastore/services_tx.go +++ b/api/datastore/services_tx.go @@ -44,7 +44,6 @@ func (tx *StoreTx) EndpointRelation() dataservices.EndpointRelationService { return tx.store.EndpointRelationService.Tx(tx.tx) } -func (tx *StoreTx) FDOProfile() dataservices.FDOProfileService { return nil } func (tx *StoreTx) HelmUserRepository() dataservices.HelmUserRepositoryService { return nil } func (tx *StoreTx) Registry() dataservices.RegistryService { diff --git a/api/datastore/test_data/output_24_to_latest.json b/api/datastore/test_data/output_24_to_latest.json index ca6694872..4469b46a6 100644 --- a/api/datastore/test_data/output_24_to_latest.json +++ b/api/datastore/test_data/output_24_to_latest.json @@ -648,12 +648,6 @@ "TemplatesURL": "", "TrustOnFirstConnect": false, "UserSessionTimeout": "8h", - "fdoConfiguration": { - "enabled": false, - "ownerPassword": "", - "ownerURL": "", - "ownerUsername": "" - }, "openAMTConfiguration": { "certFileContent": "", "certFileName": "", diff --git a/api/filesystem/filesystem.go b/api/filesystem/filesystem.go index bddfc0f61..d128347ac 100644 --- a/api/filesystem/filesystem.go +++ b/api/filesystem/filesystem.go @@ -36,8 +36,6 @@ const ( ManifestFileDefaultName = "k8s-deployment.yml" // EdgeStackStorePath represents the subfolder where edge stack files are stored in the file store folder. EdgeStackStorePath = "edge_stacks" - // FDOProfileStorePath represents the subfolder where FDO profiles files are stored in the file store folder. - FDOProfileStorePath = "fdo_profiles" // PrivateKeyFile represents the name on disk of the file containing the private key. PrivateKeyFile = "portainer.key" // PublicKeyFile represents the name on disk of the file containing the public key. @@ -1003,23 +1001,6 @@ func MoveDirectory(originalPath, newPath string, overwriteTargetPath bool) error return os.Rename(originalPath, newPath) } -// StoreFDOProfileFileFromBytes creates a subfolder in the FDOProfileStorePath and stores a new file from bytes. -// It returns the path to the folder where the file is stored. -func (service *Service) StoreFDOProfileFileFromBytes(fdoProfileIdentifier string, data []byte) (string, error) { - err := service.createDirectoryInStore(FDOProfileStorePath) - if err != nil { - return "", err - } - - filePath := JoinPaths(FDOProfileStorePath, fdoProfileIdentifier) - err = service.createFileInStore(filePath, bytes.NewReader(data)) - if err != nil { - return "", err - } - - return service.wrapFileStore(filePath), nil -} - func CreateFile(path string, r io.Reader) error { out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { diff --git a/api/hostmanagement/fdo/owner_client.go b/api/hostmanagement/fdo/owner_client.go deleted file mode 100644 index 0f3aba696..000000000 --- a/api/hostmanagement/fdo/owner_client.go +++ /dev/null @@ -1,247 +0,0 @@ -package fdo - -import ( - "bytes" - "errors" - "io" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/portainer/portainer/third_party/digest" -) - -type FDOOwnerClient struct { - OwnerURL string - Username string - Password string - Timeout time.Duration -} - -type ServiceInfo struct { - Module string - Var string - Filename string - Bytes []byte - GUID string - Device string - Priority int - OS string - Version string - Arch string - CRID int - Hash string -} - -func (c FDOOwnerClient) doDigestAuthReq(method, endpoint, contentType string, body io.Reader) (*http.Response, error) { - transport := digest.NewTransport(c.Username, c.Password) - - client, err := transport.Client() - if err != nil { - return nil, err - } - client.Timeout = c.Timeout - - e, err := url.Parse(endpoint) - if err != nil { - return nil, err - } - - u, err := url.Parse(c.OwnerURL) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(method, u.ResolveReference(e).String(), body) - if err != nil { - return nil, err - } - - if contentType != "" { - req.Header.Set("Content-Type", contentType) - } - - return client.Do(req) -} - -func (c FDOOwnerClient) PostVoucher(ov []byte) (string, error) { - resp, err := c.doDigestAuthReq( - http.MethodPost, - "api/v1/owner/vouchers", - "application/cbor", - bytes.NewReader(ov), - ) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - if resp.StatusCode != http.StatusOK { - return "", errors.New(http.StatusText(resp.StatusCode)) - } - - return string(body), nil -} - -func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error { - values := url.Values{} - values.Set("module", info.Module) - values.Set("var", info.Var) - values.Set("filename", info.Filename) - values.Set("guid", info.GUID) - values.Set("device", info.Device) - values.Set("priority", strconv.Itoa(info.Priority)) - values.Set("os", info.OS) - values.Set("version", info.Version) - values.Set("arch", info.Arch) - values.Set("crid", strconv.Itoa(info.CRID)) - values.Set("hash", info.Hash) - - resp, err := c.doDigestAuthReq( - http.MethodPut, - "api/v1/device/svi?"+values.Encode(), - "application/octet-stream", - strings.NewReader(string(info.Bytes)), - ) - if err != nil { - return err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return errors.New(http.StatusText(resp.StatusCode)) - } - - return nil -} - -func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error { - resp, err := c.doDigestAuthReq( - http.MethodPut, - "api/v1/device/svi?"+info.Encode(), - "application/octet-stream", - strings.NewReader(string(body)), - ) - if err != nil { - return err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return errors.New(http.StatusText(resp.StatusCode)) - } - - return nil -} - -func (c FDOOwnerClient) GetVouchers() ([]string, error) { - resp, err := c.doDigestAuthReq( - http.MethodGet, - "api/v1/owner/vouchers", - "", - nil, - ) - if err != nil { - return nil, err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, errors.New(http.StatusText(resp.StatusCode)) - } - - contents, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - guids := strings.FieldsFunc( - strings.TrimSpace(string(contents)), - func(c rune) bool { - return c == ',' - }, - ) - - return guids, nil -} - -func (c FDOOwnerClient) DeleteVoucher(guid string) error { - resp, err := c.doDigestAuthReq( - http.MethodDelete, - "api/v1/owner/vouchers?id="+guid, - "", - nil, - ) - if err != nil { - return err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return errors.New(http.StatusText(resp.StatusCode)) - } - - return nil -} - -func (c FDOOwnerClient) GetDeviceSVI(guid string) (string, error) { - resp, err := c.doDigestAuthReq( - http.MethodGet, - "api/v1/device/svi?guid="+guid, - "", - nil, - ) - if err != nil { - return "", err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - body, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - if resp.StatusCode != http.StatusOK { - return "", errors.New(http.StatusText(resp.StatusCode)) - } - - return string(body), nil -} - -func (c FDOOwnerClient) DeleteDeviceSVI(id string) error { - resp, err := c.doDigestAuthReq( - http.MethodDelete, - "api/v1/device/svi?id="+id, - "", - nil, - ) - if err != nil { - return err - } - - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return errors.New(http.StatusText(resp.StatusCode)) - } - - return nil -} diff --git a/api/http/handler/handler.go b/api/http/handler/handler.go index 22144cd10..9bff229c4 100644 --- a/api/http/handler/handler.go +++ b/api/http/handler/handler.go @@ -19,7 +19,6 @@ import ( "github.com/portainer/portainer/api/http/handler/file" "github.com/portainer/portainer/api/http/handler/gitops" "github.com/portainer/portainer/api/http/handler/helm" - "github.com/portainer/portainer/api/http/handler/hostmanagement/fdo" "github.com/portainer/portainer/api/http/handler/hostmanagement/openamt" "github.com/portainer/portainer/api/http/handler/kubernetes" "github.com/portainer/portainer/api/http/handler/ldap" @@ -69,7 +68,6 @@ type Handler struct { SettingsHandler *settings.Handler SSLHandler *ssl.Handler OpenAMTHandler *openamt.Handler - FDOHandler *fdo.Handler StackHandler *stacks.Handler StorybookHandler *storybook.Handler SystemHandler *system.Handler @@ -252,8 +250,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.StripPrefix("/api", h.SSLHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/open_amt"): http.StripPrefix("/api", h.OpenAMTHandler).ServeHTTP(w, r) - case strings.HasPrefix(r.URL.Path, "/api/fdo"): - http.StripPrefix("/api", h.FDOHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/teams"): http.StripPrefix("/api", h.TeamHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/team_memberships"): diff --git a/api/http/handler/hostmanagement/fdo/configure.go b/api/http/handler/hostmanagement/fdo/configure.go deleted file mode 100644 index a22c4bac8..000000000 --- a/api/http/handler/hostmanagement/fdo/configure.go +++ /dev/null @@ -1,201 +0,0 @@ -package fdo - -import ( - "encoding/hex" - "errors" - "net/http" - "net/url" - "strings" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" - - "github.com/fxamacker/cbor/v2" - "github.com/rs/zerolog/log" -) - -const ( - deploymentScriptName = "fdo.sh" -) - -type deviceConfigurePayload struct { - EdgeID string `json:"edgeID"` - EdgeKey string `json:"edgeKey"` - Name string `json:"name"` - ProfileID int `json:"profile"` -} - -func (payload *deviceConfigurePayload) Validate(r *http.Request) error { - if payload.EdgeID == "" { - return errors.New("invalid edge ID provided") - } - - if payload.EdgeKey == "" { - return errors.New("invalid edge key provided") - } - - if payload.Name == "" { - return errors.New("the device name cannot be empty") - } - - if payload.ProfileID < 1 { - return errors.New("invalid profile id provided") - } - - return nil -} - -// @id fdoConfigureDevice -// @summary configures an FDO device -// @description configures an FDO device -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @param guid path int true "Guid" -// @param body body deviceConfigurePayload true "Device Configuration" -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 403 "Permission denied to access settings" -// @failure 500 "Server error" -// @router /fdo/configure/{guid} [post] -func (handler *Handler) fdoConfigureDevice(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - guid, err := request.RetrieveRouteVariableValue(r, "guid") - if err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: request.RetrieveRouteVariableValue()") - - return httperror.InternalServerError("fdoConfigureDevice: guid not found", err) - } - - var payload deviceConfigurePayload - - err = request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - log.Error().Err(err).Msg("invalid request payload") - - return httperror.BadRequest("Invalid request payload", err) - } - - profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(payload.ProfileID)) - if handler.DataStore.IsErrObjectNotFound(err) { - return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err) - } else if err != nil { - return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err) - } - - fileContent, err := handler.FileService.GetFileContent(profile.FilePath, "") - if err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: GetFileContent") - - return httperror.InternalServerError("fdoConfigureDevice: GetFileContent", err) - } - - fdoClient, err := handler.newFDOClient() - if err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: newFDOClient()") - - return httperror.InternalServerError("fdoConfigureDevice: newFDOClient()", err) - } - - // enable fdo_sys - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"0"}, - "module": []string{"fdo_sys"}, - "var": []string{"active"}, - "bytes": []string{"F5"}, // this is "true" in CBOR - }, []byte("")); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err) - } - - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"1"}, - "module": []string{"fdo_sys"}, - "var": []string{"filedesc"}, - "filename": []string{"DEVICE_edgeid.txt"}, - }, []byte(payload.EdgeID)); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)", err) - } - - // write down the edgekey - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"1"}, - "module": []string{"fdo_sys"}, - "var": []string{"filedesc"}, - "filename": []string{"DEVICE_edgekey.txt"}, - }, []byte(payload.EdgeKey)); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)", err) - } - - // write down the device name - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"1"}, - "module": []string{"fdo_sys"}, - "var": []string{"filedesc"}, - "filename": []string{"DEVICE_name.txt"}, - }, []byte(payload.Name)); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(name)") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(name)", err) - } - - // write down the device GUID - used as the EDGE_DEVICE_GUID too - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"1"}, - "module": []string{"fdo_sys"}, - "var": []string{"filedesc"}, - "filename": []string{"DEVICE_GUID.txt"}, - }, []byte(guid)); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err) - } - - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"1"}, - "module": []string{"fdo_sys"}, - "var": []string{"filedesc"}, - "filename": []string{deploymentScriptName}, - }, fileContent); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err) - } - - b, err := cbor.Marshal([]string{"/bin/sh", deploymentScriptName}) - if err != nil { - log.Error().Err(err).Msg("failed to marshal string to CBOR") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw() failed to encode", err) - } - - cborBytes := strings.ToUpper(hex.EncodeToString(b)) - log.Debug().Str("cbor", cborBytes).Str("string", deploymentScriptName).Msg("converted to CBOR") - - if err = fdoClient.PutDeviceSVIRaw(url.Values{ - "guid": []string{guid}, - "priority": []string{"2"}, - "module": []string{"fdo_sys"}, - "var": []string{"exec"}, - "bytes": []string{cborBytes}, - }, []byte("")); err != nil { - log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()") - - return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err) - } - - return response.Empty(w) -} diff --git a/api/http/handler/hostmanagement/fdo/fdo.go b/api/http/handler/hostmanagement/fdo/fdo.go deleted file mode 100644 index eed8963df..000000000 --- a/api/http/handler/hostmanagement/fdo/fdo.go +++ /dev/null @@ -1,163 +0,0 @@ -package fdo - -import ( - "errors" - "fmt" - "net/http" - "net/url" - "strconv" - "time" - - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/hostmanagement/fdo" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" - - "github.com/rs/zerolog/log" -) - -type fdoConfigurePayload portainer.FDOConfiguration - -func validateURL(u string) error { - p, err := url.Parse(u) - if err != nil { - return err - } - - if p.Scheme != "http" && p.Scheme != "https" { - return errors.New("invalid scheme provided, must be 'http' or 'https'") - } - - if p.Host == "" { - return errors.New("invalid host provided") - } - - return nil -} - -func (payload *fdoConfigurePayload) Validate(r *http.Request) error { - if payload.Enabled { - if err := validateURL(payload.OwnerURL); err != nil { - return fmt.Errorf("owner server URL: %w", err) - } - } - - return nil -} - -func (handler *Handler) saveSettings(config portainer.FDOConfiguration) error { - settings, err := handler.DataStore.Settings().Settings() - if err != nil { - return err - } - settings.FDOConfiguration = config - - return handler.DataStore.Settings().UpdateSettings(settings) -} - -func (handler *Handler) newFDOClient() (fdo.FDOOwnerClient, error) { - settings, err := handler.DataStore.Settings().Settings() - if err != nil { - return fdo.FDOOwnerClient{}, err - } - - return fdo.FDOOwnerClient{ - OwnerURL: settings.FDOConfiguration.OwnerURL, - Username: settings.FDOConfiguration.OwnerUsername, - Password: settings.FDOConfiguration.OwnerPassword, - Timeout: 5 * time.Second, - }, nil -} - -// @id fdoConfigure -// @summary Enable Portainer's FDO capabilities -// @description Enable Portainer's FDO capabilities -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @accept json -// @produce json -// @param body body fdoConfigurePayload true "FDO Settings" -// @success 204 "Success" -// @failure 400 "Invalid request" -// @failure 403 "Permission denied to access settings" -// @failure 500 "Server error" -// @router /fdo [post] -func (handler *Handler) fdoConfigure(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - var payload fdoConfigurePayload - - err := request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - log.Error().Err(err).Msg("invalid request payload") - - return httperror.BadRequest("Invalid request payload", err) - } - - settings := portainer.FDOConfiguration(payload) - if err = handler.saveSettings(settings); err != nil { - return httperror.BadRequest("Error saving FDO settings", err) - } - - profiles, err := handler.DataStore.FDOProfile().ReadAll() - if err != nil { - return httperror.InternalServerError("Error saving FDO settings", err) - } - - if len(profiles) == 0 { - err = handler.addDefaultProfile() - if err != nil { - return httperror.InternalServerError(err.Error(), err) - } - } - - return response.Empty(w) -} - -func (handler *Handler) addDefaultProfile() error { - profileID := handler.DataStore.FDOProfile().GetNextIdentifier() - profile := &portainer.FDOProfile{ - ID: portainer.FDOProfileID(profileID), - Name: "Docker Standalone + Edge", - } - - filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(defaultProfileFileContent)) - if err != nil { - return err - } - profile.FilePath = filePath - profile.DateCreated = time.Now().Unix() - - return handler.DataStore.FDOProfile().Create(profile) -} - -const defaultProfileFileContent = ` -#!/bin/bash -ex - -env > env.log - -export AGENT_IMAGE=portainer/agent:2.11.0 -export GUID=$(cat DEVICE_GUID.txt) -export DEVICE_NAME=$(cat DEVICE_name.txt) -export EDGE_ID=$(cat DEVICE_edgeid.txt) -export EDGE_KEY=$(cat DEVICE_edgekey.txt) -export AGENTVOLUME=$(pwd)/data/portainer_agent_data/ - -mkdir -p ${AGENTVOLUME} - -docker pull ${AGENT_IMAGE} - -docker run -d \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /var/lib/docker/volumes:/var/lib/docker/volumes \ - -v /:/host \ - -v ${AGENTVOLUME}:/data \ - --restart always \ - -e EDGE=1 \ - -e EDGE_ID=${EDGE_ID} \ - -e EDGE_KEY=${EDGE_KEY} \ - -e CAP_HOST_MANAGEMENT=1 \ - -e EDGE_INSECURE_POLL=1 \ - --name portainer_edge_agent \ - ${AGENT_IMAGE} -` diff --git a/api/http/handler/hostmanagement/fdo/handler.go b/api/http/handler/hostmanagement/fdo/handler.go deleted file mode 100644 index a6304453d..000000000 --- a/api/http/handler/hostmanagement/fdo/handler.go +++ /dev/null @@ -1,40 +0,0 @@ -package fdo - -import ( - "net/http" - - portainer "github.com/portainer/portainer/api" - "github.com/portainer/portainer/api/dataservices" - "github.com/portainer/portainer/api/http/security" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - - "github.com/gorilla/mux" -) - -type Handler struct { - *mux.Router - DataStore dataservices.DataStore - FileService portainer.FileService -} - -func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStore, fileService portainer.FileService) *Handler { - h := &Handler{ - Router: mux.NewRouter(), - DataStore: dataStore, - FileService: fileService, - } - - h.Handle("/fdo/configure", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoConfigure))).Methods(http.MethodPost) - h.Handle("/fdo/list", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoListAll))).Methods(http.MethodGet) - h.Handle("/fdo/register", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoRegisterDevice))).Methods(http.MethodPost) - h.Handle("/fdo/configure/{guid}", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoConfigureDevice))).Methods(http.MethodPost) - - h.Handle("/fdo/profiles", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoProfileList))).Methods(http.MethodGet) - h.Handle("/fdo/profiles", bouncer.AdminAccess(httperror.LoggerHandler(h.createProfile))).Methods(http.MethodPost) - h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoProfileInspect))).Methods(http.MethodGet) - h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.updateProfile))).Methods(http.MethodPut) - h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.deleteProfile))).Methods(http.MethodDelete) - h.Handle("/fdo/profiles/{id}/duplicate", bouncer.AdminAccess(httperror.LoggerHandler(h.duplicateProfile))).Methods(http.MethodPost) - - return h -} diff --git a/api/http/handler/hostmanagement/fdo/list.go b/api/http/handler/hostmanagement/fdo/list.go deleted file mode 100644 index e51339009..000000000 --- a/api/http/handler/hostmanagement/fdo/list.go +++ /dev/null @@ -1,41 +0,0 @@ -package fdo - -import ( - "net/http" - - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/response" - - "github.com/rs/zerolog/log" -) - -// @id fdoListAll -// @summary List all known FDO vouchers -// @description List all known FDO vouchers -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 403 "Permission denied to access settings" -// @failure 500 "Server error" -// @router /fdo/list [get] -func (handler *Handler) fdoListAll(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - fdoClient, err := handler.newFDOClient() - if err != nil { - log.Error().Err(err).Msg("fdoListAll: newFDOClient()") - - return httperror.InternalServerError("fdoRegisterDevice: newFDOClient()", err) - } - - // Get all vouchers - guids, err := fdoClient.GetVouchers() - if err != nil { - log.Error().Err(err).Msg("fdoListAll: GetVouchers()") - - return httperror.InternalServerError("fdoListAll: GetVouchers()", err) - } - - return response.JSON(w, guids) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_create.go b/api/http/handler/hostmanagement/fdo/profile_create.go deleted file mode 100644 index 0fbabeda2..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_create.go +++ /dev/null @@ -1,95 +0,0 @@ -package fdo - -import ( - "errors" - "fmt" - "net/http" - "strconv" - "time" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -type createProfileFromFileContentPayload struct { - Name string - ProfileFileContent string -} - -func (payload *createProfileFromFileContentPayload) Validate(r *http.Request) error { - if payload.Name == "" { - return errors.New("profile name must be provided") - } - - if payload.ProfileFileContent == "" { - return errors.New("profile file content must be provided") - } - - return nil -} - -// @id createProfile -// @summary creates a new FDO Profile -// @description creates a new FDO Profile -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 409 "Profile name already exists" -// @failure 500 "Server error" -// @router /fdo/profiles [post] -func (handler *Handler) createProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - method, err := request.RetrieveQueryParameter(r, "method", false) - if err != nil { - return httperror.BadRequest("Invalid query parameter: method", err) - } - - if method == "editor" { - return handler.createFDOProfileFromFileContent(w, r) - } - - return httperror.BadRequest("Invalid method. Value must be one of: editor", errors.New("invalid method")) -} - -func (handler *Handler) createFDOProfileFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - var payload createProfileFromFileContentPayload - - err := request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - return httperror.BadRequest("Invalid request payload", err) - } - - isUnique, err := handler.checkUniqueProfileName(payload.Name, -1) - if err != nil { - return httperror.InternalServerError(err.Error(), err) - } - - if !isUnique { - return httperror.Conflict(fmt.Sprintf("A profile with the name '%s' already exists", payload.Name), errors.New("a profile already exists with this name")) - } - - profileID := handler.DataStore.FDOProfile().GetNextIdentifier() - profile := &portainer.FDOProfile{ - ID: portainer.FDOProfileID(profileID), - Name: payload.Name, - } - - filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(payload.ProfileFileContent)) - if err != nil { - return httperror.InternalServerError("Unable to persist profile file on disk", err) - } - - profile.FilePath = filePath - profile.DateCreated = time.Now().Unix() - - err = handler.DataStore.FDOProfile().Create(profile) - if err != nil { - return httperror.InternalServerError("Unable to persist the profile inside the database", err) - } - - return response.JSON(w, profile) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_delete.go b/api/http/handler/hostmanagement/fdo/profile_delete.go deleted file mode 100644 index cffe09e84..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_delete.go +++ /dev/null @@ -1,37 +0,0 @@ -package fdo - -import ( - "errors" - "net/http" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -// @id deleteProfile -// @summary deletes a FDO Profile -// @description deletes a FDO Profile -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @param id path int true "FDO Profile identifier" -// @produce json -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 500 "Server error" -// @router /fdo/profiles/{id} [delete] -func (handler *Handler) deleteProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - id, err := request.RetrieveNumericRouteVariableValue(r, "id") - if err != nil { - return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter")) - } - - err = handler.DataStore.FDOProfile().Delete(portainer.FDOProfileID(id)) - if err != nil { - return httperror.InternalServerError("Unable to delete Profile", err) - } - - return response.Empty(w) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_duplicate.go b/api/http/handler/hostmanagement/fdo/profile_duplicate.go deleted file mode 100644 index ea5402e14..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_duplicate.go +++ /dev/null @@ -1,66 +0,0 @@ -package fdo - -import ( - "errors" - "fmt" - "net/http" - "strconv" - "time" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -// @id duplicate -// @summary duplicated an existing FDO Profile -// @description duplicated an existing FDO Profile -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @param id path int true "FDO Profile identifier" -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 500 "Server error" -// @router /fdo/profiles/{id}/duplicate [post] -func (handler *Handler) duplicateProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - id, err := request.RetrieveNumericRouteVariableValue(r, "id") - if err != nil { - return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter")) - } - - originalProfile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id)) - if handler.DataStore.IsErrObjectNotFound(err) { - return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err) - } else if err != nil { - return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err) - } - - fileContent, err := handler.FileService.GetFileContent(originalProfile.FilePath, "") - if err != nil { - return httperror.InternalServerError("Unable to retrieve Profile file content", err) - } - - profileID := handler.DataStore.FDOProfile().GetNextIdentifier() - - newProfile := &portainer.FDOProfile{ - ID: portainer.FDOProfileID(profileID), - Name: fmt.Sprintf("%s - copy", originalProfile.Name), - } - - filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(newProfile.ID)), fileContent) - if err != nil { - return httperror.InternalServerError("Unable to persist profile file on disk", err) - } - newProfile.FilePath = filePath - newProfile.DateCreated = time.Now().Unix() - - err = handler.DataStore.FDOProfile().Create(newProfile) - if err != nil { - return httperror.InternalServerError("Unable to persist the profile inside the database", err) - } - - return response.JSON(w, newProfile) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_inspect.go b/api/http/handler/hostmanagement/fdo/profile_inspect.go deleted file mode 100644 index eaad731fc..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_inspect.go +++ /dev/null @@ -1,50 +0,0 @@ -package fdo - -import ( - "errors" - "net/http" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -type fdoProfileResponse struct { - Name string `json:"name"` - FileContent string `json:"fileContent"` -} - -// @id fdoProfileInspect -// @summary retrieves a given FDO profile information and content -// @description retrieves a given FDO profile information and content -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @param id path int true "FDO Profile identifier" -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 500 "Server error" -// @router /fdo/profiles/{id} [get] -func (handler *Handler) fdoProfileInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - id, err := request.RetrieveNumericRouteVariableValue(r, "id") - if err != nil { - return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter")) - } - - profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id)) - if err != nil { - return httperror.InternalServerError("Unable to retrieve Profile", err) - } - - fileContent, err := handler.FileService.GetFileContent(profile.FilePath, "") - if err != nil { - return httperror.InternalServerError("Unable to retrieve Profile file content", err) - } - - return response.JSON(w, fdoProfileResponse{ - Name: profile.Name, - FileContent: string(fileContent), - }) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_list.go b/api/http/handler/hostmanagement/fdo/profile_list.go deleted file mode 100644 index 3828b27c7..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_list.go +++ /dev/null @@ -1,31 +0,0 @@ -package fdo - -import ( - "net/http" - - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -// @id fdoProfileList -// @summary retrieves all FDO profiles -// @description retrieves all FDO profiles -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 403 "Permission denied to access settings" -// @failure 500 "Server error" -// @failure 500 "Bad gateway" -// @router /fdo/profiles [get] -func (handler *Handler) fdoProfileList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - - profiles, err := handler.DataStore.FDOProfile().ReadAll() - if err != nil { - return httperror.InternalServerError(err.Error(), err) - } - - return response.JSON(w, profiles) -} diff --git a/api/http/handler/hostmanagement/fdo/profile_update.go b/api/http/handler/hostmanagement/fdo/profile_update.go deleted file mode 100644 index 6af0bb007..000000000 --- a/api/http/handler/hostmanagement/fdo/profile_update.go +++ /dev/null @@ -1,68 +0,0 @@ -package fdo - -import ( - "errors" - "fmt" - "net/http" - "strconv" - - portainer "github.com/portainer/portainer/api" - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" -) - -// @id updateProfile -// @summary updates an existing FDO Profile -// @description updates an existing FDO Profile -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @param id path int true "FDO Profile identifier" -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 409 "Profile name already exists" -// @failure 500 "Server error" -// @router /fdo/profiles/{id} [put] -func (handler *Handler) updateProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - id, err := request.RetrieveNumericRouteVariableValue(r, "id") - if err != nil { - return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter")) - } - - var payload createProfileFromFileContentPayload - err = request.DecodeAndValidateJSONPayload(r, &payload) - if err != nil { - return httperror.BadRequest("Invalid request payload", err) - } - - profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id)) - if handler.DataStore.IsErrObjectNotFound(err) { - return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err) - } else if err != nil { - return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err) - } - - isUnique, err := handler.checkUniqueProfileName(payload.Name, id) - if err != nil { - return httperror.InternalServerError(err.Error(), err) - } - if !isUnique { - return httperror.Conflict(fmt.Sprintf("A profile with the name '%s' already exists", payload.Name), errors.New("a profile already exists with this name")) - } - - filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(payload.ProfileFileContent)) - if err != nil { - return httperror.InternalServerError("Unable to update profile", err) - } - profile.FilePath = filePath - profile.Name = payload.Name - - err = handler.DataStore.FDOProfile().Update(profile.ID, profile) - if err != nil { - return httperror.InternalServerError("Unable to update profile", err) - } - - return response.JSON(w, profile) -} diff --git a/api/http/handler/hostmanagement/fdo/register.go b/api/http/handler/hostmanagement/fdo/register.go deleted file mode 100644 index 088a25394..000000000 --- a/api/http/handler/hostmanagement/fdo/register.go +++ /dev/null @@ -1,53 +0,0 @@ -package fdo - -import ( - "net/http" - - httperror "github.com/portainer/portainer/pkg/libhttp/error" - "github.com/portainer/portainer/pkg/libhttp/request" - "github.com/portainer/portainer/pkg/libhttp/response" - - "github.com/rs/zerolog/log" -) - -type registerDeviceResponse struct { - Guid string `json:"guid" example:"c6ea3343-229a-4c07-9096-beef7134e1d3"` -} - -// @id fdoRegisterDevice -// @summary register an FDO device -// @description register an FDO device -// @description **Access policy**: administrator -// @tags intel -// @security jwt -// @produce json -// @success 200 "Success" -// @failure 400 "Invalid request" -// @failure 403 "Permission denied to access settings" -// @failure 500 "Server error" -// @router /fdo/register [post] -func (handler *Handler) fdoRegisterDevice(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { - // Post a voucher - ov, filename, err := request.RetrieveMultiPartFormFile(r, "voucher") - if err != nil { - log.Info().Str("filename", filename).Err(err).Msg("fdoRegisterDevice: readVoucher()") - - return httperror.InternalServerError("fdoRegisterDevice: read Voucher()", err) - } - - fdoClient, err := handler.newFDOClient() - if err != nil { - log.Info().Err(err).Msg("fdoRegisterDevice: newFDOClient()") - - return httperror.InternalServerError("fdoRegisterDevice: newFDOClient()", err) - } - - guid, err := fdoClient.PostVoucher(ov) - if err != nil { - log.Info().Err(err).Msg("fdoRegisterDevice: PostVoucher()") - - return httperror.InternalServerError("fdoRegisterDevice: PostVoucher()", err) - } - - return response.JSON(w, registerDeviceResponse{guid}) -} diff --git a/api/http/handler/hostmanagement/fdo/utils.go b/api/http/handler/hostmanagement/fdo/utils.go deleted file mode 100644 index 665a904bd..000000000 --- a/api/http/handler/hostmanagement/fdo/utils.go +++ /dev/null @@ -1,16 +0,0 @@ -package fdo - -func (handler *Handler) checkUniqueProfileName(name string, id int) (bool, error) { - profiles, err := handler.DataStore.FDOProfile().ReadAll() - if err != nil { - return false, err - } - - for _, profile := range profiles { - if profile.Name == name && (id == -1 || id != int(profile.ID)) { - return false, nil - } - } - - return true, nil -} diff --git a/api/http/handler/settings/settings_public.go b/api/http/handler/settings/settings_public.go index 61f27c025..69e72f45d 100644 --- a/api/http/handler/settings/settings_public.go +++ b/api/http/handler/settings/settings_public.go @@ -36,8 +36,6 @@ type publicSettingsResponse struct { // Whether team sync is enabled TeamSync bool `json:"TeamSync" example:"true"` - // Whether FDO is enabled - IsFDOEnabled bool // Whether AMT is enabled IsAMTEnabled bool @@ -86,7 +84,6 @@ func generatePublicSettings(appSettings *portainer.Settings) *publicSettingsResp EnableTelemetry: appSettings.EnableTelemetry, KubeconfigExpiry: appSettings.KubeconfigExpiry, Features: featureflags.FeatureFlags(), - IsFDOEnabled: appSettings.EnableEdgeComputeFeatures && appSettings.FDOConfiguration.Enabled, IsAMTEnabled: appSettings.EnableEdgeComputeFeatures && appSettings.OpenAMTConfiguration.Enabled, } diff --git a/api/http/server.go b/api/http/server.go index bff5cf2fd..83355793b 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -32,7 +32,6 @@ import ( "github.com/portainer/portainer/api/http/handler/file" "github.com/portainer/portainer/api/http/handler/gitops" "github.com/portainer/portainer/api/http/handler/helm" - "github.com/portainer/portainer/api/http/handler/hostmanagement/fdo" "github.com/portainer/portainer/api/http/handler/hostmanagement/openamt" kubehandler "github.com/portainer/portainer/api/http/handler/kubernetes" "github.com/portainer/portainer/api/http/handler/ldap" @@ -239,8 +238,6 @@ func (server *Server) Start() error { openAMTHandler.DataStore = server.DataStore openAMTHandler.DockerClientFactory = server.DockerClientFactory - fdoHandler := fdo.NewHandler(requestBouncer, server.DataStore, server.FileService) - var stackHandler = stacks.NewHandler(requestBouncer) stackHandler.DataStore = server.DataStore stackHandler.DockerClientFactory = server.DockerClientFactory @@ -316,7 +313,6 @@ func (server *Server) Start() error { KubernetesHandler: kubernetesHandler, MOTDHandler: motdHandler, OpenAMTHandler: openAMTHandler, - FDOHandler: fdoHandler, RegistryHandler: registryHandler, ResourceControlHandler: resourceControlHandler, SettingsHandler: settingsHandler, diff --git a/api/internal/testhelpers/datastore.go b/api/internal/testhelpers/datastore.go index dd33799f0..f0bba23fd 100644 --- a/api/internal/testhelpers/datastore.go +++ b/api/internal/testhelpers/datastore.go @@ -17,7 +17,6 @@ type testDatastore struct { endpoint dataservices.EndpointService endpointGroup dataservices.EndpointGroupService endpointRelation dataservices.EndpointRelationService - fdoProfile dataservices.FDOProfileService helmUserRepository dataservices.HelmUserRepositoryService registry dataservices.RegistryService resourceControl dataservices.ResourceControlService @@ -55,10 +54,6 @@ func (d *testDatastore) EdgeStack() dataservices.EdgeStackService { re func (d *testDatastore) Endpoint() dataservices.EndpointService { return d.endpoint } func (d *testDatastore) EndpointGroup() dataservices.EndpointGroupService { return d.endpointGroup } -func (d *testDatastore) FDOProfile() dataservices.FDOProfileService { - return d.fdoProfile -} - func (d *testDatastore) EndpointRelation() dataservices.EndpointRelationService { return d.endpointRelation } diff --git a/api/portainer.go b/api/portainer.go index 0f5aba27a..d5652be3f 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -96,24 +96,6 @@ type ( // PowerState represents an AMT managed device power state PowerState int - FDOConfiguration struct { - Enabled bool `json:"enabled"` - OwnerURL string `json:"ownerURL"` - OwnerUsername string `json:"ownerUsername"` - OwnerPassword string `json:"ownerPassword"` - } - - // FDOProfileID represents a fdo profile id - FDOProfileID int - - FDOProfile struct { - ID FDOProfileID `json:"id"` - Name string `json:"name"` - FilePath string `json:"filePath"` - NumberDevices int `json:"numberDevices"` - DateCreated int64 `json:"dateCreated"` - } - // CLIFlags represents the available flags on the CLI CLIFlags struct { Addr *string @@ -963,7 +945,6 @@ type ( LDAPSettings LDAPSettings `json:"LDAPSettings"` OAuthSettings OAuthSettings `json:"OAuthSettings"` OpenAMTConfiguration OpenAMTConfiguration `json:"openAMTConfiguration"` - FDOConfiguration FDOConfiguration `json:"fdoConfiguration"` FeatureFlagSettings map[featureflags.Feature]bool `json:"FeatureFlagSettings"` // The interval in which environment(endpoint) snapshots are created SnapshotInterval string `json:"SnapshotInterval" example:"5m"` @@ -1456,7 +1437,6 @@ type ( StoreSSLCertPair(cert, key []byte) (string, string, error) CopySSLCertPair(certPath, keyPath string) (string, string, error) CopySSLCACert(caCertPath string) (string, error) - StoreFDOProfileFileFromBytes(fdoProfileIdentifier string, data []byte) (string, error) StoreMTLSCertificates(cert, caCert, key []byte) (string, string, string, error) GetDefaultChiselPrivateKeyPath() string StoreChiselPrivateKey(privateKey []byte) error @@ -1646,13 +1626,7 @@ const ( ) // List of supported features -const ( - FeatureFdo = "fdo" -) - -var SupportedFeatureFlags = []featureflags.Feature{ - FeatureFdo, -} +var SupportedFeatureFlags = []featureflags.Feature{} const ( _ AuthenticationMethod = iota diff --git a/app/portainer/__module.js b/app/portainer/__module.js index da42183eb..f44af2c47 100644 --- a/app/portainer/__module.js +++ b/app/portainer/__module.js @@ -200,17 +200,6 @@ angular }, }; - var deviceImport = { - name: 'portainer.endpoints.importDevice', - url: '/device', - views: { - 'content@': { - templateUrl: './views/devices/import/importDevice.html', - controller: 'ImportDeviceController', - }, - }, - }; - const edgeAutoCreateScript = { name: 'portainer.endpoints.edgeAutoCreateScript', url: '/aeec', @@ -224,26 +213,6 @@ angular }, }; - var addFDOProfile = { - name: 'portainer.endpoints.profile', - url: '/profile', - views: { - 'content@': { - component: 'addProfileView', - }, - }, - }; - - var editFDOProfile = { - name: 'portainer.endpoints.profile.edit', - url: '/:id', - views: { - 'content@': { - component: 'editProfileView', - }, - }, - }; - var endpointAccess = { name: 'portainer.endpoints.endpoint.access', url: '/access', @@ -484,9 +453,6 @@ angular $stateRegistryProvider.register(endpointAccess); $stateRegistryProvider.register(endpointKVM); $stateRegistryProvider.register(edgeAutoCreateScript); - $stateRegistryProvider.register(deviceImport); - $stateRegistryProvider.register(addFDOProfile); - $stateRegistryProvider.register(editFDOProfile); $stateRegistryProvider.register(groups); $stateRegistryProvider.register(group); $stateRegistryProvider.register(groupAccess); diff --git a/app/portainer/hostmanagement/fdo/fdo.service.ts b/app/portainer/hostmanagement/fdo/fdo.service.ts deleted file mode 100644 index b5d9554b3..000000000 --- a/app/portainer/hostmanagement/fdo/fdo.service.ts +++ /dev/null @@ -1,99 +0,0 @@ -import axios, { parseAxiosError } from '@/portainer/services/axios'; - -import { FDOConfiguration, DeviceConfiguration, Profile } from './model'; - -const BASE_URL = '/fdo'; - -export async function configureFDO(formValues: FDOConfiguration) { - try { - await axios.post(`${BASE_URL}/configure`, formValues); - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to configure FDO'); - } -} - -export async function configureDevice( - deviceId: string, - deviceConfig: DeviceConfiguration -) { - try { - await axios.post(`${BASE_URL}/configure/${deviceId}`, deviceConfig); - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to configure device'); - } -} - -export async function createProfile( - name: string, - method: string, - profileFileContent: string -) { - const payload = { - name, - profileFileContent, - }; - try { - await axios.post(`${BASE_URL}/profiles`, payload, { - params: { method }, - }); - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to create profile'); - } -} - -export async function getProfiles() { - try { - const { data: profiles } = await axios.get( - `${BASE_URL}/profiles` - ); - return profiles; - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve the profiles'); - } -} - -export async function getProfile(profileId: number) { - try { - const { data: profile } = await axios.get( - `${BASE_URL}/profiles/${profileId}` - ); - return profile; - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to retrieve profile'); - } -} - -export async function deleteProfile(profileId: number) { - try { - await axios.delete(`${BASE_URL}/profiles/${profileId}`); - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to delete profile'); - } -} - -export async function updateProfile( - id: number, - name: string, - profileFileContent: string -) { - const payload = { - name, - profileFileContent, - }; - try { - await axios.put(`${BASE_URL}/profiles/${id}`, payload); - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to update profile'); - } -} - -export async function duplicateProfile(id: number) { - try { - const { data: profile } = await axios.post( - `${BASE_URL}/profiles/${id}/duplicate` - ); - return profile; - } catch (e) { - throw parseAxiosError(e as Error, 'Unable to duplicate profile'); - } -} diff --git a/app/portainer/hostmanagement/fdo/model.ts b/app/portainer/hostmanagement/fdo/model.ts deleted file mode 100644 index e757847b3..000000000 --- a/app/portainer/hostmanagement/fdo/model.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface FDOConfiguration { - enabled: boolean; - ownerURL: string; - ownerUsername: string; - ownerPassword: string; -} - -export interface DeviceConfiguration { - edgeID: string; - edgeKey: string; - name: string; - profile: string; -} - -export type Profile = { - id: number; - name: string; - fileContent: string; - dateCreated: string; -}; diff --git a/app/portainer/models/settings.js b/app/portainer/models/settings.js index 0d1bda9fa..08cbc4ec8 100644 --- a/app/portainer/models/settings.js +++ b/app/portainer/models/settings.js @@ -6,7 +6,6 @@ export function SettingsViewModel(data) { this.LDAPSettings = data.LDAPSettings; this.OAuthSettings = new OAuthSettingsViewModel(data.OAuthSettings); this.openAMTConfiguration = data.openAMTConfiguration; - this.fdoConfiguration = data.fdoConfiguration; this.SnapshotInterval = data.SnapshotInterval; this.TemplatesURL = data.TemplatesURL; this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval; @@ -37,7 +36,6 @@ export function PublicSettingsViewModel(settings) { this.Edge = new EdgeSettingsViewModel(settings.Edge); this.DefaultRegistry = settings.DefaultRegistry; this.IsAMTEnabled = settings.IsAMTEnabled; - this.IsFDOEnabled = settings.IsFDOEnabled; } export function InternalAuthSettingsViewModel(data) { diff --git a/app/portainer/react/components/settings.ts b/app/portainer/react/components/settings.ts index 4abe4a3b7..478050dcd 100644 --- a/app/portainer/react/components/settings.ts +++ b/app/portainer/react/components/settings.ts @@ -1,6 +1,5 @@ import angular from 'angular'; -import { SettingsFDO } from '@/react/portainer/settings/EdgeComputeView/SettingsFDO'; import { SettingsOpenAMT } from '@/react/portainer/settings/EdgeComputeView/SettingsOpenAMT'; import { InternalAuth } from '@/react/portainer/settings/AuthenticationView/InternalAuth'; import { r2a } from '@/react-tools/react2angular'; @@ -17,10 +16,6 @@ import { AuthStyleField } from '@/react/portainer/settings/AuthenticationView/OA export const settingsModule = angular .module('portainer.app.react.components.settings', []) - .component( - 'settingsFdo', - r2a(withUIRouter(withReactQuery(SettingsFDO)), ['onSubmit', 'settings']) - ) .component('settingsOpenAmt', r2a(SettingsOpenAMT, ['onSubmit', 'settings'])) .component( 'internalAuth', diff --git a/app/portainer/services/fileUpload.js b/app/portainer/services/fileUpload.js index 302595c0c..e69ad7dc0 100644 --- a/app/portainer/services/fileUpload.js +++ b/app/portainer/services/fileUpload.js @@ -195,15 +195,5 @@ function FileUploadFactory($q, Upload) { return $q.all(queue); }; - service.uploadOwnershipVoucher = function (voucherFile) { - return Upload.upload({ - url: 'api/fdo/register', - data: { - voucher: voucherFile, - }, - ignoreLoadingBar: true, - }); - }; - return service; } diff --git a/app/portainer/views/devices/import/importDevice.html b/app/portainer/views/devices/import/importDevice.html deleted file mode 100644 index 0fba94119..000000000 --- a/app/portainer/views/devices/import/importDevice.html +++ /dev/null @@ -1,237 +0,0 @@ - - -
-
- - - -
- - -

- - You are setting up a Portainer Edge Agent that will initiate the communications with the Portainer instance and your FDO Devices. -

-
- - -
Import Voucher
-
-
- -

- - Import one or more Manufacturer's Ownership Vouchers to initiate device attestation -

-
-
- -
-
-
-
-

Connecting to the Owner service...

-
-
-
-
-

Ownership Voucher Uploaded

-
-
-
- - -
Device details
-
- -

- - Device name will serve as your reference name in Portainer -

-
- -
- -
- -
-
-
-
-
-

- - This field is required.

-
-
-
- - - -

- - Suffix starting number will be appended to the end of the Device name, if initiating multiple devices this will be incrementally increased -

-
-
- -
- -
-
-
-
-
-

- - This field is required.

-

- - This field needs to be a positive integer number.

-
-
-
- - -
- -
- -
-
-
-
-
-

- - This field is required.

-
-
-
- -
- -
- -
- -
-
- - - -
Set up Tags
-
- -

- - This is just an option if your device is under a certain group -

-
- -
- -
- -
-
- - - - - -
-
- - Cancel -
-
- -
-
-
-
-
-
diff --git a/app/portainer/views/devices/import/importDeviceController.js b/app/portainer/views/devices/import/importDeviceController.js deleted file mode 100644 index 6770196f1..000000000 --- a/app/portainer/views/devices/import/importDeviceController.js +++ /dev/null @@ -1,138 +0,0 @@ -import uuidv4 from 'uuid/v4'; - -import { PortainerEndpointCreationTypes } from 'Portainer/models/endpoint/models'; -import { configureDevice, getProfiles } from 'Portainer/hostmanagement/fdo/fdo.service'; - -angular - .module('portainer.app') - .controller( - 'ImportDeviceController', - function ImportDeviceController($async, $q, $scope, $state, EndpointService, GroupService, TagService, Notifications, Authentication, FileUploadService) { - $scope.state = { - actionInProgress: false, - vouchersUploading: false, - vouchersUploaded: false, - deviceIDs: [], - allowCreateTag: Authentication.isAdmin(), - }; - - $scope.formValues = { - DeviceName: '', - DeviceProfile: '', - GroupId: 1, - TagIds: [], - VoucherFiles: [], - PortainerURL: '', - Suffix: 1, - }; - - $scope.profiles = []; - - $scope.onChangeTags = function onChangeTags(value) { - return $scope.$evalAsync(() => { - $scope.formValues.TagIds = value; - }); - }; - - $scope.onVoucherFilesChange = function () { - if ($scope.formValues.VoucherFiles.length < 1) { - return; - } - - $scope.state.vouchersUploading = true; - - let uploads = $scope.formValues.VoucherFiles.map((f) => FileUploadService.uploadOwnershipVoucher(f)); - - $q.all(uploads) - .then(function success(responses) { - $scope.state.vouchersUploading = false; - $scope.state.vouchersUploaded = true; - $scope.state.deviceIDs = responses.map((r) => r.data.guid); - }) - .catch(function error(err) { - $scope.state.vouchersUploading = false; - if ($scope.formValues.VoucherFiles.length === 1) { - Notifications.error('Failure', err, 'Unable to upload the Ownership Voucher'); - } else { - Notifications.error('Failure', null, 'Unable to upload the Ownership Vouchers, please check the logs'); - } - }); - }; - - $scope.createEndpointAndConfigureDevice = function () { - return $async(async () => { - $scope.state.actionInProgress = true; - - let suffix = $scope.formValues.Suffix; - - for (const deviceID of $scope.state.deviceIDs) { - let deviceName = $scope.formValues.DeviceName + suffix; - - try { - var endpoint = await EndpointService.createRemoteEndpoint( - deviceName, - PortainerEndpointCreationTypes.EdgeAgentEnvironment, - $scope.formValues.PortainerURL, - '', - $scope.formValues.GroupId, - $scope.formValues.TagIds, - false, - false, - false, - null, - null, - null, - null - ); - } catch (err) { - Notifications.error('Failure', err, 'Unable to create the environment'); - $scope.state.actionInProgress = false; - return; - } - - suffix++; - - const config = { - edgeID: endpoint.EdgeID || uuidv4(), - edgeKey: endpoint.EdgeKey, - name: deviceName, - profile: $scope.formValues.DeviceProfile, - }; - - try { - await configureDevice(deviceID, config); - } catch (err) { - Notifications.error('Failure', err, 'Unable to import device'); - return; - } finally { - $scope.state.actionInProgress = false; - } - } - - Notifications.success('Success', 'Device(s) successfully imported'); - $state.go('edge.devices'); - }); - }; - - async function initView() { - try { - $scope.profiles = await getProfiles(); - } catch (err) { - Notifications.error('Failure', err, 'Unable to load profiles'); - return; - } - - $q.all({ - groups: GroupService.groups(), - }) - .then(function success(data) { - $scope.groups = data.groups; - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to load groups'); - }); - } - - initView(); - } - ); diff --git a/app/portainer/views/devices/profiles/add/addProfile.html b/app/portainer/views/devices/profiles/add/addProfile.html deleted file mode 100644 index 05d192a49..000000000 --- a/app/portainer/views/devices/profiles/add/addProfile.html +++ /dev/null @@ -1,65 +0,0 @@ - - -
-
- - -
- -
Device Profile Details
-
- -
- -
-
- - -
Profile configuration
- - - - - - - - -
-
- Cancel - -
-
- -
-
-
-
-
diff --git a/app/portainer/views/devices/profiles/add/addProfileController.js b/app/portainer/views/devices/profiles/add/addProfileController.js deleted file mode 100644 index d2f2e8077..000000000 --- a/app/portainer/views/devices/profiles/add/addProfileController.js +++ /dev/null @@ -1,70 +0,0 @@ -import angular from 'angular'; -import { editor } from '@@/BoxSelector/common-options/build-methods'; - -import { createProfile } from 'Portainer/hostmanagement/fdo/fdo.service'; - -angular.module('portainer.app').controller('AddProfileController', AddProfileController); - -/* @ngInject */ -export default function AddProfileController($scope, $async, $state, $window, Notifications) { - $scope.buildMethods = [editor]; - - $scope.formValues = { - name: '', - profileFileContent: '', - }; - - $scope.state = { - method: 'editor', - actionInProgress: false, - isEditorDirty: false, - }; - - $window.onbeforeunload = () => { - if ($scope.state.method === 'editor' && $scope.formValues.profileFileContent && $scope.state.isEditorDirty) { - return ''; - } - }; - - $scope.$on('$destroy', function () { - $scope.state.isEditorDirty = false; - }); - - $scope.onChangeFormValues = onChangeFormValues; - - $scope.createProfileAsync = function () { - return $async(async () => { - const method = $scope.state.method; - - const name = $scope.formValues.name; - const fileContent = $scope.formValues.profileFileContent; - - if (method !== 'editor' && fileContent === '') { - $scope.state.formValidationError = 'Profile file content must not be empty'; - return; - } - - $scope.state.actionInProgress = true; - - try { - await createProfile(name, method, fileContent); - Notifications.success('Success', 'Profile successfully created'); - $scope.state.isEditorDirty = false; - $state.go('portainer.settings.edgeCompute'); - } catch (err) { - Notifications.error('Failure', err, 'Unable to create Profile'); - } finally { - $scope.state.actionInProgress = false; - } - }); - }; - - $scope.onChangeFileContent = function onChangeFileContent(value) { - $scope.formValues.profileFileContent = value; - $scope.state.isEditorDirty = true; - }; - - function onChangeFormValues(newValues) { - $scope.formValues = newValues; - } -} diff --git a/app/portainer/views/devices/profiles/add/index.js b/app/portainer/views/devices/profiles/add/index.js deleted file mode 100644 index 2cb6a3416..000000000 --- a/app/portainer/views/devices/profiles/add/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import angular from 'angular'; - -import controller from './addProfileController'; - -angular.module('portainer.app').component('addProfileView', { - templateUrl: './addProfile.html', - controller, -}); diff --git a/app/portainer/views/devices/profiles/edit/editProfile.html b/app/portainer/views/devices/profiles/edit/editProfile.html deleted file mode 100644 index 1e811e475..000000000 --- a/app/portainer/views/devices/profiles/edit/editProfile.html +++ /dev/null @@ -1,66 +0,0 @@ - - -
-
- - -
- -
Device Profile Details
-
- -
- -
-
- - -
Profile configuration
- - - - - - - - - -
-
- Cancel - -
-
- -
-
-
-
-
diff --git a/app/portainer/views/devices/profiles/edit/editProfileController.js b/app/portainer/views/devices/profiles/edit/editProfileController.js deleted file mode 100644 index 929754f10..000000000 --- a/app/portainer/views/devices/profiles/edit/editProfileController.js +++ /dev/null @@ -1,88 +0,0 @@ -import angular from 'angular'; -import { editor } from '@@/BoxSelector/common-options/build-methods'; -import { getProfile, updateProfile } from 'Portainer/hostmanagement/fdo/fdo.service'; - -angular.module('portainer.app').controller('EditProfileController', EditProfileController); - -/* @ngInject */ -export default function EditProfileController($scope, $async, $state, $window, Notifications) { - $scope.buildMethods = [editor]; - - $scope.formValues = { - name: '', - profileFileContent: '', - }; - - $scope.state = { - profileID: $state.params.id, - method: 'editor', - actionInProgress: false, - isEditorDirty: false, - }; - - $window.onbeforeunload = () => { - if ($scope.state.method === 'editor' && $scope.formValues.profileFileContent && $scope.state.isEditorDirty) { - return ''; - } - }; - - $scope.$on('$destroy', function () { - $scope.state.isEditorDirty = false; - }); - - $scope.onChangeFormValues = onChangeFormValues; - - $scope.updateProfileAsync = function () { - return $async(async () => { - const method = $scope.state.method; - - const name = $scope.formValues.name; - const fileContent = $scope.formValues.profileFileContent; - - if (method !== 'editor' && fileContent === '') { - $scope.state.formValidationError = 'Profile file content must not be empty'; - return; - } - - $scope.state.actionInProgress = true; - - try { - await updateProfile($scope.state.profileID, name, fileContent); - Notifications.success('Success', 'Profile successfully updated'); - $scope.state.isEditorDirty = false; - $state.go('portainer.settings.edgeCompute'); - } catch (err) { - Notifications.error('Failure', err, 'Unable to update Profile'); - } finally { - $scope.state.actionInProgress = false; - } - }); - }; - - $scope.onChangeFileContent = function onChangeFileContent(value) { - $scope.formValues.profileFileContent = value; - $scope.state.isEditorDirty = true; - }; - - function onChangeFormValues(newValues) { - $scope.formValues = newValues; - } - - async function initView() { - return $async(async () => { - try { - const profile = await getProfile($scope.state.profileID); - - $scope.formValues = { - name: profile.name, - profileFileContent: profile.fileContent, - }; - $scope.state.isEditorDirty = false; - } catch (err) { - Notifications.error('Failure', err, 'Unable to retrieve profile details'); - } - }); - } - - initView(); -} diff --git a/app/portainer/views/devices/profiles/edit/index.js b/app/portainer/views/devices/profiles/edit/index.js deleted file mode 100644 index 93d551c9d..000000000 --- a/app/portainer/views/devices/profiles/edit/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import angular from 'angular'; - -import controller from './editProfileController'; - -angular.module('portainer.app').component('editProfileView', { - templateUrl: './editProfile.html', - controller, -}); diff --git a/app/portainer/views/settings/edge-compute/settingsEdgeCompute.html b/app/portainer/views/settings/edge-compute/settingsEdgeCompute.html index 028621d41..21d437fd3 100644 --- a/app/portainer/views/settings/edge-compute/settingsEdgeCompute.html +++ b/app/portainer/views/settings/edge-compute/settingsEdgeCompute.html @@ -11,9 +11,3 @@ - -
-
- -
-
diff --git a/app/portainer/views/settings/edge-compute/settingsEdgeComputeController.js b/app/portainer/views/settings/edge-compute/settingsEdgeComputeController.js index b570765e3..ad0b6527b 100644 --- a/app/portainer/views/settings/edge-compute/settingsEdgeComputeController.js +++ b/app/portainer/views/settings/edge-compute/settingsEdgeComputeController.js @@ -1,7 +1,6 @@ import _ from 'lodash-es'; import angular from 'angular'; -import { configureFDO } from '@/portainer/hostmanagement/fdo/fdo.service'; import { configureAMT } from 'Portainer/hostmanagement/open-amt/open-amt.service'; angular.module('portainer.app').controller('SettingsEdgeComputeController', SettingsEdgeComputeController); @@ -31,16 +30,6 @@ export default function SettingsEdgeComputeController($q, $async, $state, Notifi } }; - this.onSubmitFDO = async function (formValues) { - try { - await configureFDO(formValues); - Notifications.success('Success', `FDO successfully ${formValues.enabled ? 'enabled' : 'disabled'}`); - $state.reload(); - } catch (err) { - Notifications.error('Failure', err, 'Failed applying changes'); - } - }; - function initView() { $async(async () => { try { diff --git a/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx b/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx index 5817ff4be..ea459b2fc 100644 --- a/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx +++ b/app/react/portainer/environments/ListView/EnvironmentsDatatable.tsx @@ -15,7 +15,6 @@ import { EnvironmentStatus } from '../types'; import { columns } from './columns'; import { EnvironmentListItem } from './types'; -import { ImportFdoDeviceButton } from './ImportFdoDeviceButton'; const tableKey = 'environments'; const settingsStore = createPersistedStore(tableKey, 'Name'); @@ -83,8 +82,6 @@ export function EnvironmentsDatatable({ Remove - - {isBE && ( settings.fdoConfiguration.enabled, - flagEnabledQuery.data - ); - - if (!isFDOEnabledQuery.data || !flagEnabledQuery.data) { - return null; - } - - return ( -
- - Import FDO device - -
- ); -} diff --git a/app/react/portainer/feature-flags/useFeatureFlag.ts b/app/react/portainer/feature-flags/useFeatureFlag.ts index 9cf6c720e..e2e24c1f0 100644 --- a/app/react/portainer/feature-flags/useFeatureFlag.ts +++ b/app/react/portainer/feature-flags/useFeatureFlag.ts @@ -1,8 +1,6 @@ import { usePublicSettings } from '../settings/queries'; -export enum FeatureFlag { - FDO = 'fdo', -} +export enum FeatureFlag {} export function useFeatureFlag( flag: FeatureFlag, diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatable.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatable.tsx deleted file mode 100644 index 43803a6fc..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatable.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { List } from 'lucide-react'; - -import { Datatable } from '@@/datatables'; -import { createPersistedStore } from '@@/datatables/types'; -import { useTableState } from '@@/datatables/useTableState'; - -import { columns } from './columns'; -import { FDOProfilesDatatableActions } from './FDOProfilesDatatableActions'; -import { useFDOProfiles } from './useFDOProfiles'; - -const storageKey = 'fdoProfiles'; - -const settingsStore = createPersistedStore(storageKey, 'name'); - -export interface FDOProfilesDatatableProps { - isFDOEnabled: boolean; -} - -export function FDOProfilesDatatable({ - isFDOEnabled, -}: FDOProfilesDatatableProps) { - const tableState = useTableState(settingsStore, storageKey); - - const { isLoading, profiles } = useFDOProfiles(); - - return ( - row.id.toString()} - isLoading={isLoading} - renderTableActions={(selectedItems) => ( - - )} - data-cy="fdo-profiles-datatable" - /> - ); -} diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx deleted file mode 100644 index 08dcf8e21..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/FDOProfilesDatatableActions.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { useQueryClient } from '@tanstack/react-query'; -import { useRouter } from '@uirouter/react'; -import { PlusCircle } from 'lucide-react'; - -import { Profile } from '@/portainer/hostmanagement/fdo/model'; -import * as notifications from '@/portainer/services/notifications'; -import { - deleteProfile, - duplicateProfile, -} from '@/portainer/hostmanagement/fdo/fdo.service'; - -import { confirm } from '@@/modals/confirm'; -import { Link } from '@@/Link'; -import { Button } from '@@/buttons'; -import { DeleteButton } from '@@/buttons/DeleteButton'; - -interface Props { - isFDOEnabled: boolean; - selectedItems: Profile[]; -} - -export function FDOProfilesDatatableActions({ - isFDOEnabled, - selectedItems, -}: Props) { - const router = useRouter(); - const queryClient = useQueryClient(); - - return ( - <> - - - - - - - onDeleteProfileClick()} - confirmMessage="This action will delete the selected profile(s). Continue?" - data-cy="fdo-remove-profile-button" - /> - - ); - - async function onDuplicateProfileClick() { - const confirmed = await confirm({ - title: 'Are you sure ?', - message: 'This action will duplicate the selected profile. Continue?', - }); - - if (!confirmed) { - return; - } - - try { - const profile = selectedItems[0]; - const newProfile = await duplicateProfile(profile.id); - notifications.success('Profile successfully duplicated', profile.name); - router.stateService.go('portainer.endpoints.profile.edit', { - id: newProfile.id, - }); - } catch (err) { - notifications.error( - 'Failure', - err as Error, - 'Unable to duplicate profile' - ); - } - } - - async function onDeleteProfileClick() { - await Promise.all( - selectedItems.map(async (profile) => { - try { - await deleteProfile(profile.id); - - notifications.success('Profile successfully removed', profile.name); - } catch (err) { - notifications.error( - 'Failure', - err as Error, - 'Unable to remove profile' - ); - } - }) - ); - - await queryClient.invalidateQueries(['fdo_profiles']); - } -} diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/created.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/created.tsx deleted file mode 100644 index bc793f5b6..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/created.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { isoDateFromTimestamp } from '@/portainer/filters/filters'; - -import { columnHelper } from './helper'; - -export const created = columnHelper.accessor('dateCreated', { - header: 'Created', - id: 'created', - cell: ({ getValue }) => { - const value = getValue(); - return isoDateFromTimestamp(value); - }, -}); diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/helper.ts b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/helper.ts deleted file mode 100644 index 02266610d..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/helper.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createColumnHelper } from '@tanstack/react-table'; - -import { Profile } from '@/portainer/hostmanagement/fdo/model'; - -export const columnHelper = createColumnHelper(); diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/index.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/index.tsx deleted file mode 100644 index 7c10c2483..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/columns/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Profile } from '@/portainer/hostmanagement/fdo/model'; - -import { buildNameColumn } from '@@/datatables/buildNameColumn'; - -import { created } from './created'; - -export const columns = [ - buildNameColumn( - 'name', - 'portainer.endpoints.profile.edit', - 'fdo-profiles-name' - ), - created, -]; diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/index.ts b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/index.ts deleted file mode 100644 index 4507a2791..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { FDOProfilesDatatable } from './FDOProfilesDatatable'; diff --git a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/useFDOProfiles.tsx b/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/useFDOProfiles.tsx deleted file mode 100644 index 04f669500..000000000 --- a/app/react/portainer/settings/EdgeComputeView/FDOProfilesDatatable/useFDOProfiles.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useEffect, useMemo } from 'react'; -import { useQuery } from '@tanstack/react-query'; - -import PortainerError from '@/portainer/error'; -import * as notifications from '@/portainer/services/notifications'; -import { getProfiles } from '@/portainer/hostmanagement/fdo/fdo.service'; - -export function useFDOProfiles() { - const { isLoading, data, isError, error } = useQuery(['fdo_profiles'], () => - getProfiles() - ); - - useEffect(() => { - if (isError) { - notifications.error( - 'Failure', - error as Error, - 'Failed retrieving FDO profiles' - ); - } - }, [isError, error]); - - const profiles = useMemo(() => data || [], [data]); - - return { - isLoading, - profiles, - error: isError ? (error as PortainerError) : undefined, - }; -} diff --git a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.module.css b/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.module.css deleted file mode 100644 index faec054ae..000000000 --- a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.fdo-table { - margin-top: 3em; -} diff --git a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.tsx b/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.tsx deleted file mode 100644 index ea61f5005..000000000 --- a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Formik, Field, Form } from 'formik'; -import { FlaskConical, Laptop } from 'lucide-react'; - -import { FDOConfiguration } from '@/portainer/hostmanagement/fdo/model'; -import { - FeatureFlag, - useFeatureFlag, -} from '@/react/portainer/feature-flags/useFeatureFlag'; - -import { Switch } from '@@/form-components/SwitchField/Switch'; -import { FormControl } from '@@/form-components/FormControl'; -import { FormSectionTitle } from '@@/form-components/FormSectionTitle'; -import { Widget, WidgetBody, WidgetTitle } from '@@/Widget'; -import { LoadingButton } from '@@/buttons/LoadingButton'; -import { TextTip } from '@@/Tip/TextTip'; -import { Input } from '@@/form-components/Input'; - -import { FDOProfilesDatatable } from '../FDOProfilesDatatable'; - -import styles from './SettingsFDO.module.css'; -import { validationSchema } from './SettingsFDO.validation'; - -export interface Settings { - fdoConfiguration: FDOConfiguration; - EnableEdgeComputeFeatures: boolean; -} - -interface Props { - settings: Settings; - onSubmit(values: FDOConfiguration): void; -} - -export function SettingsFDO({ settings, onSubmit }: Props) { - const flagEnabledQuery = useFeatureFlag(FeatureFlag.FDO); - - if (!flagEnabledQuery.data) { - return ( - - - - Since FDO is still an experimental feature that requires additional - infrastructure, it has been temporarily hidden in the UI. - - - - ); - } - - return ; -} - -export function SettingsFDOForm({ settings, onSubmit }: Props) { - const fdoConfiguration = settings ? settings.fdoConfiguration : null; - const initialFDOEnabled = fdoConfiguration ? fdoConfiguration.enabled : false; - - const [isFDOEnabled, setIsFDOEnabled] = useState(initialFDOEnabled); - useEffect(() => { - setIsFDOEnabled(settings?.fdoConfiguration?.enabled); - }, [settings]); - - const initialValues = { - enabled: initialFDOEnabled, - ownerURL: fdoConfiguration ? fdoConfiguration.ownerURL : '', - ownerUsername: fdoConfiguration ? fdoConfiguration.ownerUsername : '', - ownerPassword: fdoConfiguration ? fdoConfiguration.ownerPassword : '', - }; - - const edgeComputeFeaturesEnabled = settings - ? settings.EnableEdgeComputeFeatures - : false; - - return ( -
- - - - validationSchema()} - validateOnChange - validateOnMount - > - {({ - values, - errors, - handleSubmit, - setFieldValue, - isSubmitting, - isValid, - dirty, - }) => ( -
- - onChangedEnabled(e, setFieldValue)} - /> - - - - When enabled, this will allow Portainer to interact with FDO - Services. - - - {edgeComputeFeaturesEnabled && values.enabled && ( - <> -
- - - - - - - - - - - - - - )} - -
-
- - Save settings - -
-
-
- )} -
- - {edgeComputeFeaturesEnabled && isFDOEnabled && ( -
- Device Profiles - - Add, Edit and Manage the list of device profiles available - during FDO device setup - - -
- )} -
-
-
- ); - - async function onChangedEnabled( - e: boolean, - setFieldValue: ( - field: string, - value: unknown, - shouldValidate?: boolean - ) => void - ) { - setIsFDOEnabled(e); - setFieldValue('enabled', e); - } -} diff --git a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.validation.ts b/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.validation.ts deleted file mode 100644 index 15ab2dda0..000000000 --- a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/SettingsFDO.validation.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { object, string } from 'yup'; - -export function validationSchema() { - return object().shape({ - ownerURL: string().when('enabled', { - is: true, - then: string().required('Field is required'), - }), - ownerUsername: string().when('enabled', { - is: true, - then: string().required('Field is required'), - }), - ownerPassword: string().when('enabled', { - is: true, - then: string().required('Field is required'), - }), - }); -} diff --git a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/index.ts b/app/react/portainer/settings/EdgeComputeView/SettingsFDO/index.ts deleted file mode 100644 index 600c80856..000000000 --- a/app/react/portainer/settings/EdgeComputeView/SettingsFDO/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SettingsFDO } from './SettingsFDO'; diff --git a/app/react/portainer/settings/EdgeComputeView/SettingsOpenAMT/SettingsOpenAMT.tsx b/app/react/portainer/settings/EdgeComputeView/SettingsOpenAMT/SettingsOpenAMT.tsx index d5eb6b671..6aa72ea32 100644 --- a/app/react/portainer/settings/EdgeComputeView/SettingsOpenAMT/SettingsOpenAMT.tsx +++ b/app/react/portainer/settings/EdgeComputeView/SettingsOpenAMT/SettingsOpenAMT.tsx @@ -253,7 +253,7 @@ export function SettingsOpenAMT({ settings, onSubmit }: Props) {
diff --git a/app/react/portainer/settings/types.ts b/app/react/portainer/settings/types.ts index 4fe3e64e0..7bb8f381f 100644 --- a/app/react/portainer/settings/types.ts +++ b/app/react/portainer/settings/types.ts @@ -1,12 +1,5 @@ import { TeamId } from '@/react/portainer/users/teams/types'; -export interface FDOConfiguration { - enabled: boolean; - ownerURL: string; - ownerUsername: string; - ownerPassword: string; -} - export interface TLSConfiguration { TLS: boolean; TLSSkipVerify: boolean; @@ -119,7 +112,6 @@ export interface Settings { LDAPSettings: LDAPSettings; OAuthSettings: OAuthSettings; openAMTConfiguration: OpenAMTConfiguration; - fdoConfiguration: FDOConfiguration; FeatureFlagSettings: { [key: Feature]: boolean }; SnapshotInterval: string; TemplatesURL: string; @@ -201,8 +193,6 @@ export interface PublicSettingsResponse { KubeconfigExpiry: string; /** Whether team sync is enabled */ TeamSync: boolean; - /** Whether FDO is enabled */ - IsFDOEnabled: boolean; /** Whether AMT is enabled */ IsAMTEnabled: boolean; /** Whether to hide default registry (only on BE) */