1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-24 15:59:41 +02:00

chore(fdo): remove FDO code EE-7235 (#11981)

This commit is contained in:
andres-portainer 2024-06-28 08:42:16 -03:00 committed by GitHub
parent 1a3db327c7
commit 19fa40286a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 3 additions and 2609 deletions

View file

@ -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)
}

View file

@ -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}
`

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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),
})
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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})
}

View file

@ -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
}