Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion internal/controller/httpapi/ui_noui.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ package httpapi
import (
"net/http"

"github.com/gin-gonic/gin"

"github.com/device-management-toolkit/console/config"
"github.com/device-management-toolkit/console/pkg/logger"
"github.com/gin-gonic/gin"
)

func HasUI() bool { return false }
Expand Down
43 changes: 43 additions & 0 deletions internal/controller/httpapi/v1/boot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package v1

import (
"net/http"

"github.com/gin-gonic/gin"

"github.com/device-management-toolkit/console/internal/entity/dto/v1"
)

func (r *deviceManagementRoutes) getRemoteEraseCapabilities(c *gin.Context) {
guid := c.Param("guid")

capabilities, err := r.d.GetRemoteEraseCapabilities(c.Request.Context(), guid)
if err != nil {
r.l.Error(err, "http - v1 - getRemoteEraseCapabilities")
ErrorResponse(c, err)

return
}

c.JSON(http.StatusOK, capabilities)
}

func (r *deviceManagementRoutes) setRemoteEraseOptions(c *gin.Context) {
guid := c.Param("guid")

var req dto.RemoteEraseRequest
if err := c.ShouldBindJSON(&req); err != nil {
ErrorResponse(c, err)

return
}

if err := r.d.SetRemoteEraseOptions(c.Request.Context(), guid, req); err != nil {
r.l.Error(err, "http - v1 - setRemoteEraseOptions")
ErrorResponse(c, err)

return
}

c.JSON(http.StatusOK, nil)
}
147 changes: 147 additions & 0 deletions internal/controller/httpapi/v1/boot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package v1

import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"

"github.com/device-management-toolkit/console/internal/entity/dto/v1"
"github.com/device-management-toolkit/console/internal/mocks"
)

func TestGetRemoteEraseCapabilities(t *testing.T) {
t.Parallel()

tests := []struct {
name string
mock func(m *mocks.MockDeviceManagementFeature)
expectedCode int
response interface{}
}{
{
name: "getRemoteEraseCapabilities - successful retrieval",
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().GetRemoteEraseCapabilities(context.Background(), "valid-guid").
Return(dto.BootCapabilities{SecureEraseAllSSDs: true}, nil)
},
expectedCode: http.StatusOK,
response: dto.BootCapabilities{SecureEraseAllSSDs: true},
},
{
name: "getRemoteEraseCapabilities - service failure",
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().GetRemoteEraseCapabilities(context.Background(), "valid-guid").
Return(dto.BootCapabilities{}, ErrGeneral)
},
expectedCode: http.StatusInternalServerError,
response: nil,
},
}

for _, tc := range tests {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

deviceManagement, engine := deviceManagementTest(t)
tc.mock(deviceManagement)

req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/api/v1/amt/boot/remoteErase/valid-guid", http.NoBody)
require.NoError(t, err)

w := httptest.NewRecorder()
engine.ServeHTTP(w, req)

require.Equal(t, tc.expectedCode, w.Code)

if tc.expectedCode == http.StatusOK {
jsonBytes, _ := json.Marshal(tc.response)
require.Equal(t, string(jsonBytes), w.Body.String())
}
})
}
}

func TestSetRemoteEraseOptions(t *testing.T) {
t.Parallel()

tests := []struct {
name string
requestBody interface{}
mock func(m *mocks.MockDeviceManagementFeature)
expectedCode int
}{
{
name: "setRemoteEraseOptions - successful",
requestBody: dto.RemoteEraseRequest{SecureEraseAllSSDs: true, TPMClear: true},
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().SetRemoteEraseOptions(context.Background(), "valid-guid", dto.RemoteEraseRequest{SecureEraseAllSSDs: true, TPMClear: true}).
Return(nil)
},
expectedCode: http.StatusOK,
},
{
name: "setRemoteEraseOptions - forwards full payload",
requestBody: dto.RemoteEraseRequest{
SecureEraseAllSSDs: true,
TPMClear: true,
RestoreBIOSToEOM: true,
UnconfigureCSME: true,
SSDPassword: "s3cr3t",
},
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().SetRemoteEraseOptions(context.Background(), "valid-guid", dto.RemoteEraseRequest{
SecureEraseAllSSDs: true,
TPMClear: true,
RestoreBIOSToEOM: true,
UnconfigureCSME: true,
SSDPassword: "s3cr3t",
}).
Return(nil)
},
expectedCode: http.StatusOK,
},
{
name: "setRemoteEraseOptions - invalid JSON payload",
requestBody: "invalid-json",
mock: func(_ *mocks.MockDeviceManagementFeature) {
},
expectedCode: http.StatusInternalServerError,
},
{
name: "setRemoteEraseOptions - service failure",
requestBody: dto.RemoteEraseRequest{UnconfigureCSME: true},
mock: func(m *mocks.MockDeviceManagementFeature) {
m.EXPECT().SetRemoteEraseOptions(context.Background(), "valid-guid", dto.RemoteEraseRequest{UnconfigureCSME: true}).
Return(ErrGeneral)
},
expectedCode: http.StatusInternalServerError,
},
}

for _, tc := range tests {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

deviceManagement, engine := deviceManagementTest(t)
tc.mock(deviceManagement)

reqBody, _ := json.Marshal(tc.requestBody)
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/api/v1/amt/boot/remoteErase/valid-guid", bytes.NewBuffer(reqBody))
require.NoError(t, err)

w := httptest.NewRecorder()
engine.ServeHTTP(w, req)

require.Equal(t, tc.expectedCode, w.Code)
})
}
}
127 changes: 71 additions & 56 deletions internal/controller/httpapi/v1/devicemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,60 +20,75 @@ func NewAmtRoutes(handler *gin.RouterGroup, d devices.Feature, amt amtexplorer.F
r := &deviceManagementRoutes{d, amt, e, l}

h := handler.Group("/amt")
{
h.GET("version/:guid", r.getVersion)

h.GET("features/:guid", r.getFeatures)
h.POST("features/:guid", r.setFeatures)

h.GET("alarmOccurrences/:guid", r.getAlarmOccurrences)
h.POST("alarmOccurrences/:guid", r.createAlarmOccurrences)
h.DELETE("alarmOccurrences/:guid", r.deleteAlarmOccurrences)

h.GET("hardwareInfo/:guid", r.getHardwareInfo)
h.GET("diskInfo/:guid", r.getDiskInfo)
h.GET("power/state/:guid", r.getPowerState)
h.POST("power/action/:guid", r.powerAction)
h.POST("power/bootOptions/:guid", r.setBootOptions)
h.POST("power/bootoptions/:guid", r.setBootOptions)
h.GET("power/bootSources/:guid", r.getBootSources)
h.GET("power/capabilities/:guid", r.getPowerCapabilities)

h.GET("log/audit/:guid", r.getAuditLog)
h.GET("log/audit/:guid/download", r.downloadAuditLog)
h.GET("log/event/:guid", r.getEventLog)
h.GET("log/event/:guid/download", r.downloadEventLog)
h.GET("generalSettings/:guid", r.getGeneralSettings)

h.GET("userConsentCode/cancel/:guid", r.cancelUserConsentCode)
h.GET("userConsentCode/:guid", r.getUserConsentCode)
h.POST("userConsentCode/:guid", r.sendConsentCode)

h.GET("networkSettings/:guid", r.getNetworkSettings)
h.GET("networkSettings/wired/:guid", r.getWiredNetworkSettings)
h.PATCH("networkSettings/wired/:guid", r.patchWiredNetworkSettings)
h.GET("networkSettings/wireless/state/:guid", r.getWirelessState)
h.POST("networkSettings/wireless/state/:guid", r.requestWirelessStateChange)
h.GET("networkSettings/wireless/profileSync/:guid", r.getWirelessProfileSync)
h.POST("networkSettings/wireless/profileSync/:guid", r.setWirelessProfileSync)
h.GET("networkSettings/wireless/profile/:guid", r.getWirelessProfiles)
h.POST("networkSettings/wireless/profile/:guid", r.addWirelessProfile)
h.PATCH("networkSettings/wireless/profile/:guid", r.updateWirelessProfile)
h.DELETE("networkSettings/wireless/profile/:guid/:profileName", r.deleteWirelessProfile)

h.GET("explorer", r.getCallList)
h.GET("explorer/:guid/:call", r.executeCall)
h.GET("tls/:guid", r.getTLSSettingData)

h.GET("certificates/:guid", r.getCertificates)
h.POST("certificates/:guid", r.addCertificate)
h.DELETE("certificates/:guid/:instanceId", r.deleteCertificate)

// KVM display settings
h.GET("kvm/displays/:guid", r.getKVMDisplays)
h.PUT("kvm/displays/:guid", r.setKVMDisplays)

// Network link preference
h.POST("network/linkPreference/:guid", r.setLinkPreference)
}
r.registerCoreRoutes(h)
r.registerPowerAndLogRoutes(h)
r.registerNetworkRoutes(h)
r.registerExplorerAndCertificateRoutes(h)
r.registerKVMAndLinkRoutes(h)
}

func (r *deviceManagementRoutes) registerCoreRoutes(h *gin.RouterGroup) {
h.GET("version/:guid", r.getVersion)

h.GET("features/:guid", r.getFeatures)
h.POST("features/:guid", r.setFeatures)

h.GET("alarmOccurrences/:guid", r.getAlarmOccurrences)
h.POST("alarmOccurrences/:guid", r.createAlarmOccurrences)
h.DELETE("alarmOccurrences/:guid", r.deleteAlarmOccurrences)

h.GET("boot/remoteErase/:guid", r.getRemoteEraseCapabilities)
h.POST("boot/remoteErase/:guid", r.setRemoteEraseOptions)
h.GET("hardwareInfo/:guid", r.getHardwareInfo)
h.GET("diskInfo/:guid", r.getDiskInfo)
h.GET("generalSettings/:guid", r.getGeneralSettings)

h.GET("userConsentCode/cancel/:guid", r.cancelUserConsentCode)
h.GET("userConsentCode/:guid", r.getUserConsentCode)
h.POST("userConsentCode/:guid", r.sendConsentCode)
}

func (r *deviceManagementRoutes) registerPowerAndLogRoutes(h *gin.RouterGroup) {
h.GET("power/state/:guid", r.getPowerState)
h.POST("power/action/:guid", r.powerAction)
h.POST("power/bootOptions/:guid", r.setBootOptions)
h.POST("power/bootoptions/:guid", r.setBootOptions)
h.GET("power/bootSources/:guid", r.getBootSources)
h.GET("power/capabilities/:guid", r.getPowerCapabilities)

h.GET("log/audit/:guid", r.getAuditLog)
h.GET("log/audit/:guid/download", r.downloadAuditLog)
h.GET("log/event/:guid", r.getEventLog)
h.GET("log/event/:guid/download", r.downloadEventLog)
}

func (r *deviceManagementRoutes) registerNetworkRoutes(h *gin.RouterGroup) {
h.GET("networkSettings/:guid", r.getNetworkSettings)
h.GET("networkSettings/wired/:guid", r.getWiredNetworkSettings)
h.PATCH("networkSettings/wired/:guid", r.patchWiredNetworkSettings)
h.GET("networkSettings/wireless/state/:guid", r.getWirelessState)
h.POST("networkSettings/wireless/state/:guid", r.requestWirelessStateChange)
h.GET("networkSettings/wireless/profileSync/:guid", r.getWirelessProfileSync)
h.POST("networkSettings/wireless/profileSync/:guid", r.setWirelessProfileSync)
h.GET("networkSettings/wireless/profile/:guid", r.getWirelessProfiles)
h.POST("networkSettings/wireless/profile/:guid", r.addWirelessProfile)
h.PATCH("networkSettings/wireless/profile/:guid", r.updateWirelessProfile)
h.DELETE("networkSettings/wireless/profile/:guid/:profileName", r.deleteWirelessProfile)
}

func (r *deviceManagementRoutes) registerExplorerAndCertificateRoutes(h *gin.RouterGroup) {
h.GET("explorer", r.getCallList)
h.GET("explorer/:guid/:call", r.executeCall)
h.GET("tls/:guid", r.getTLSSettingData)

h.GET("certificates/:guid", r.getCertificates)
h.POST("certificates/:guid", r.addCertificate)
h.DELETE("certificates/:guid/:instanceId", r.deleteCertificate)
}

func (r *deviceManagementRoutes) registerKVMAndLinkRoutes(h *gin.RouterGroup) {
h.GET("kvm/displays/:guid", r.getKVMDisplays)
h.PUT("kvm/displays/:guid", r.setKVMDisplays)

h.POST("network/linkPreference/:guid", r.setLinkPreference)
}
2 changes: 1 addition & 1 deletion internal/controller/httpapi/v1/devicemanagement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestDeviceManagement(t *testing.T) {
OCR: false,
OptInState: 0,
Redirection: false,
RemoteErase: false,
RPE: false,
UserConsent: "",
WinREBootSupported: false,
},
Expand Down
3 changes: 2 additions & 1 deletion internal/controller/httpapi/v1/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (r *deviceManagementRoutes) getFeatures(c *gin.Context) {
HTTPSBootSupported: features.HTTPSBootSupported,
WinREBootSupported: features.WinREBootSupported,
LocalPBABootSupported: features.LocalPBABootSupported,
RemoteErase: features.RemoteErase,
RPE: features.RPE,
RPESupported: features.RPESupported,
}

c.JSON(http.StatusOK, v1Features)
Expand Down
Loading
Loading