Skip to content

Commit

Permalink
feat: added get certificates api (#135)
Browse files Browse the repository at this point in the history
Co-authored-by: rjbrache <rick.bracher@intel.com>
Co-authored-by: madhavilosetty-intel <madhavi.losetty@intel.com>
  • Loading branch information
3 people committed Jun 20, 2024
1 parent f8ee20a commit 7e7bfd8
Show file tree
Hide file tree
Showing 9 changed files with 1,382 additions and 6 deletions.
14 changes: 14 additions & 0 deletions internal/controller/http/v1/devicemanagement.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func newAmtRoutes(handler *gin.RouterGroup, d devices.Feature, l logger.Interfac
h.POST("userConsentCode/:guid", r.sendConsentCode)

h.GET("networkSettings/:guid", r.getNetworkSettings)
h.GET("certificates/:guid", r.getCertificates)
}
}

Expand Down Expand Up @@ -357,3 +358,16 @@ func (r *deviceManagementRoutes) getNetworkSettings(c *gin.Context) {

c.JSON(http.StatusOK, network)
}

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

certs, err := r.d.GetCertificates(c.Request.Context(), guid)
if err != nil {
errorResponse(c, err)

return
}

c.JSON(http.StatusOK, certs)
}
37 changes: 34 additions & 3 deletions internal/controller/http/v1/devicemanagement_mocks_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions internal/controller/http/v1/devicemanagement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -19,6 +20,8 @@ import (
"github.com/open-amt-cloud-toolkit/console/pkg/logger"
)

var ErrGeneral = errors.New("general error")

func deviceManagementTest(t *testing.T) (*MockDeviceManagementFeature, *gin.Engine) {
t.Helper()

Expand Down Expand Up @@ -194,6 +197,28 @@ func TestGetNetworkSettings(t *testing.T) {
expectedCode: http.StatusOK,
response: map[string]interface{}{"": ""},
},
{
name: "getCertificates - successful retrieval",
url: "/api/v1/amt/certificates/valid-guid",
method: http.MethodGet,
mock: func(m *MockDeviceManagementFeature) {
m.EXPECT().GetCertificates(context.Background(), "valid-guid").
Return(map[string]interface{}{"": ""}, nil)
},
expectedCode: http.StatusOK,
response: map[string]interface{}{"": ""},
},
{
name: "getCertificates - failed retrieval",
url: "/api/v1/amt/certificates/valid-guid",
method: http.MethodGet,
mock: func(m *MockDeviceManagementFeature) {
m.EXPECT().GetCertificates(context.Background(), "valid-guid").
Return(nil, ErrGeneral)
},
expectedCode: http.StatusInternalServerError,
response: nil,
},
}

for _, tc := range tests {
Expand Down
174 changes: 174 additions & 0 deletions internal/usecase/devices/certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package devices

import (
"context"
"reflect"
"strings"

"github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/wsman/amt/publickey"
"github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/wsman/amt/publicprivate"
"github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/wsman/cim/concrete"
"github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/wsman/cim/credential"

"github.com/open-amt-cloud-toolkit/console/internal/usecase/devices/wsman"
)

const (
TypeWireless string = "Wireless"
TypeTLS string = "TLS"
TypeWired string = "Wired"
)

type SecuritySettings struct {
ProfileAssociation []ProfileAssociation `json:"ProfileAssociation"`
Certificates interface{} `json:"Certificates"`
Keys interface{} `json:"PublicKeys"`
}

type ProfileAssociation struct {
Type string `json:"Type"`
ProfileID string `json:"ProfileID"`
RootCertificate interface{} `json:"RootCertificate,omitempty"`
ClientCertificate interface{} `json:"ClientCertificate,omitempty"`
Key interface{} `json:"PublicKey,omitempty"`
}

func processConcreteDependencies(certificateHandle string, profileAssociation *ProfileAssociation, dependancyItems []concrete.ConcreteDependency, keyPairItems []publicprivate.RefinedPublicPrivateKeyPair) {
for x := range dependancyItems {
if dependancyItems[x].Antecedent.ReferenceParameters.SelectorSet.Selectors[0].Text != certificateHandle {
continue
}

keyHandle := dependancyItems[x].Dependent.ReferenceParameters.SelectorSet.Selectors[0].Text

for i := range keyPairItems {
if keyPairItems[i].InstanceID == keyHandle {
profileAssociation.Key = keyPairItems[i]

break
}
}
}
}

func buildCertificateAssociations(profileAssociation ProfileAssociation, securitySettings *SecuritySettings) {
var publicKeyHandle string

// If a client cert, update the associated public key w/ the cert's handle
if profileAssociation.ClientCertificate != nil {
// Loop thru public keys looking for the one that matches the current profileAssociation's key
for i, existingKeyPair := range securitySettings.Keys.(publicprivate.RefinedPullResponse).PublicPrivateKeyPairItems {
// If found update that key with the profileAssociation's certificate handle
if existingKeyPair.InstanceID == profileAssociation.Key.(publicprivate.RefinedPublicPrivateKeyPair).InstanceID {
securitySettings.Keys.(publicprivate.RefinedPullResponse).PublicPrivateKeyPairItems[i].CertificateHandle = profileAssociation.ClientCertificate.(publickey.RefinedPublicKeyCertificateResponse).InstanceID
// save this public key handle since we know it pairs with the profileAssociation's certificate
publicKeyHandle = securitySettings.Keys.(publicprivate.RefinedPullResponse).PublicPrivateKeyPairItems[i].InstanceID

break
}
}
}

// Loop thru certificates looking for the one that matches the current profileAssociation's certificate and append profile name
for i := range securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems {
if (profileAssociation.ClientCertificate != nil && securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].InstanceID == profileAssociation.ClientCertificate.(publickey.RefinedPublicKeyCertificateResponse).InstanceID) ||
(profileAssociation.RootCertificate != nil && securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].InstanceID == profileAssociation.RootCertificate.(publickey.RefinedPublicKeyCertificateResponse).InstanceID) {
// if client cert found, associate the previously found key handle with it
if !securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].TrustedRootCertificate {
securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].PublicKeyHandle = publicKeyHandle
}

securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].AssociatedProfiles = append(securitySettings.Certificates.(publickey.RefinedPullResponse).PublicKeyCertificateItems[i].AssociatedProfiles, profileAssociation.ProfileID)

break
}
}
}

func buildProfileAssociations(certificateHandle string, profileAssociation *ProfileAssociation, response wsman.Certificates, securitySettings *SecuritySettings) {
isNewProfileAssociation := true

for idx := range response.PublicKeyCertificateResponse.PublicKeyCertificateItems {
if response.PublicKeyCertificateResponse.PublicKeyCertificateItems[idx].InstanceID != certificateHandle {
continue
}

if response.PublicKeyCertificateResponse.PublicKeyCertificateItems[idx].TrustedRootCertificate {
profileAssociation.RootCertificate = response.PublicKeyCertificateResponse.PublicKeyCertificateItems[idx]

continue
}

profileAssociation.ClientCertificate = response.PublicKeyCertificateResponse.PublicKeyCertificateItems[idx]

processConcreteDependencies(certificateHandle, profileAssociation, response.ConcreteDependencyResponse.Items, response.PublicPrivateKeyPairResponse.PublicPrivateKeyPairItems)
}

// Check if the certificate is already in the list
for idx := range securitySettings.ProfileAssociation {
if !(securitySettings.ProfileAssociation[idx].ProfileID == profileAssociation.ProfileID) {
continue
}

if profileAssociation.RootCertificate != nil {
securitySettings.ProfileAssociation[idx].RootCertificate = profileAssociation.RootCertificate
}

if profileAssociation.ClientCertificate != nil {
securitySettings.ProfileAssociation[idx].ClientCertificate = profileAssociation.ClientCertificate
}

if profileAssociation.Key != nil {
securitySettings.ProfileAssociation[idx].Key = profileAssociation.Key
}

isNewProfileAssociation = false

break
}

// If the profile is not in the list, add it
if isNewProfileAssociation {
securitySettings.ProfileAssociation = append(securitySettings.ProfileAssociation, *profileAssociation)
}
}

func processCertificates(contextItems []credential.CredentialContext, response wsman.Certificates, profileType string, securitySettings *SecuritySettings) {
for idx := range contextItems {
var profileAssociation ProfileAssociation

profileAssociation.Type = profileType
profileAssociation.ProfileID = strings.TrimPrefix(contextItems[idx].ElementProvidingContext.ReferenceParameters.SelectorSet.Selectors[0].Text, "Intel(r) AMT:IEEE 802.1x Settings ")
certificateHandle := contextItems[idx].ElementInContext.ReferenceParameters.SelectorSet.Selectors[0].Text

buildProfileAssociations(certificateHandle, &profileAssociation, response, securitySettings)
buildCertificateAssociations(profileAssociation, securitySettings)
}
}

func (uc *UseCase) GetCertificates(c context.Context, guid string) (interface{}, error) {
item, err := uc.GetByID(c, guid, "")
if err != nil || item.GUID == "" {
return nil, err
}

uc.device.SetupWsmanClient(*item, false, true)

response, err := uc.device.GetCertificates()
if err != nil {
return nil, err
}

securitySettings := SecuritySettings{
Certificates: response.PublicKeyCertificateResponse,
Keys: response.PublicPrivateKeyPairResponse,
}

if !reflect.DeepEqual(response.CIMCredentialContextResponse, credential.PullResponse{}) {
processCertificates(response.CIMCredentialContextResponse.Items.CredentialContextTLS, response, TypeTLS, &securitySettings)
processCertificates(response.CIMCredentialContextResponse.Items.CredentialContext, response, TypeWireless, &securitySettings)
processCertificates(response.CIMCredentialContextResponse.Items.CredentialContext8021x, response, TypeWired, &securitySettings)
}

return securitySettings, nil
}
Loading

0 comments on commit 7e7bfd8

Please sign in to comment.