diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b3fe5b5..ea89f58e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -58,9 +58,9 @@ jobs: id: args run: | if ${{ github.ref_type == 'tag' }} ; then - echo "args=release --rm-dist" >> $GITHUB_OUTPUT + echo "args=release --clean" >> $GITHUB_OUTPUT else - echo "args=release --rm-dist --skip-validate --skip-publish" >> $GITHUB_OUTPUT + echo "args=release --clean --skip=validate --skip=publish" >> $GITHUB_OUTPUT fi - name: Run GoReleaser diff --git a/Makefile b/Makefile index b1930e35..9b2cf051 100644 --- a/Makefile +++ b/Makefile @@ -104,7 +104,7 @@ inject-web: $(CLIENT_APPLICATION_BINARY_PATH) .PHONY: inject-web build: - UI_FILE=$(UI_FILE) UI_SEPARATOR=$(UI_SEPARATOR) goreleaser build --rm-dist --single-target --skip-validate + UI_FILE=$(UI_FILE) UI_SEPARATOR=$(UI_SEPARATOR) goreleaser build --clean --single-target --skip=validate .PHONY: build test: env diff --git a/service/grpc/onboardDevice.go b/service/grpc/onboardDevice.go index 0a9d3ba3..e35c48bd 100644 --- a/service/grpc/onboardDevice.go +++ b/service/grpc/onboardDevice.go @@ -28,10 +28,10 @@ import ( "github.com/plgd-dev/device/v2/schema" "github.com/plgd-dev/device/v2/schema/acl" "github.com/plgd-dev/device/v2/schema/cloud" + "github.com/plgd-dev/device/v2/schema/credential" "github.com/plgd-dev/device/v2/schema/maintenance" "github.com/plgd-dev/device/v2/schema/softwareupdate" "github.com/plgd-dev/kit/v2/security" - "github.com/plgd-dev/kit/v2/strings" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) @@ -151,6 +151,38 @@ func onboardSecureDevice(ctx context.Context, dev *device, links schema.Resource }) } +func insecureAddCredentials(ctx context.Context, cloudID string, dev *device, links schema.ResourceLinks, cert string) error { + links = links.GetResourceLinks(credential.ResourceType) + if len(links) == 0 { + // add credential resource is not supported by device + return nil + } + _, err := security.ParseX509FromPEM([]byte(cert)) + if err != nil { + return fmt.Errorf("cannot parse CA for device %v: %w", dev.DeviceID(), err) + } + link := links[0] + link.Endpoints = link.Endpoints.FilterUnsecureEndpoints() + setCaCredential := credential.CredentialUpdateRequest{ + Credentials: []credential.Credential{ + { + Subject: cloudID, + Type: credential.CredentialType_ASYMMETRIC_SIGNING_WITH_CERTIFICATE, + Usage: credential.CredentialUsage_TRUST_CA, + PublicData: &credential.CredentialPublicData{ + DataInternal: cert, + Encoding: credential.CredentialPublicDataEncoding_PEM, + }, + }, + }, + } + err = dev.UpdateResource(ctx, link, setCaCredential, nil, coap.WithDeviceID(dev.DeviceID())) + if err != nil { + return fmt.Errorf("cannot add CA to credential resource %v of device %v: %w", link.Href, dev.DeviceID(), err) + } + return nil +} + func onboardInsecureDevice(ctx context.Context, dev *device, links schema.ResourceLinks, req *pb.OnboardDeviceRequest) error { switch { case req.GetAuthorizationProviderName() == "": @@ -160,18 +192,20 @@ func onboardInsecureDevice(ctx context.Context, dev *device, links schema.Resour case req.GetCoapGatewayAddress() == "": return fmt.Errorf("invalid URL") } - var link schema.ResourceLink - - for _, l := range links { - if strings.SliceContains(l.ResourceTypes, cloud.ResourceType) { - link = l - break - } - } - if link.Href == "" { + cloudLinks := links.GetResourceLinks(cloud.ResourceType) + if len(cloudLinks) == 0 { return fmt.Errorf("could not resolve cloud resource link of device %s", dev.DeviceID()) } + link := cloudLinks[0] link.Endpoints = link.Endpoints.FilterUnsecureEndpoints() + + if len(req.GetCertificateAuthorities()) > 0 { + err := insecureAddCredentials(ctx, req.GetHubId(), dev, links, req.GetCertificateAuthorities()) + if err != nil { + return err + } + } + err := dev.UpdateResource(ctx, link, cloud.ConfigurationUpdateRequest{ AuthorizationProvider: req.GetAuthorizationProviderName(), AuthorizationCode: req.GetAuthorizationCode(), diff --git a/service/grpc/onboardDevice_internal_test.go b/service/grpc/onboardDevice_internal_test.go index 7503279b..3f66ab4f 100644 --- a/service/grpc/onboardDevice_internal_test.go +++ b/service/grpc/onboardDevice_internal_test.go @@ -18,14 +18,19 @@ package grpc import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "strings" "testing" "time" "github.com/plgd-dev/client-application/pb" "github.com/plgd-dev/device/v2/client/core" + "github.com/plgd-dev/device/v2/pkg/security/generateCertificate" "github.com/plgd-dev/device/v2/schema" "github.com/plgd-dev/device/v2/schema/cloud" + "github.com/plgd-dev/device/v2/schema/credential" "github.com/stretchr/testify/require" ) @@ -51,3 +56,37 @@ func TestOnboardInsecureDevice(t *testing.T) { require.Error(t, err) require.True(t, strings.Contains(err.Error(), "could not set cloud resource of device")) } + +func TestOnboardInsecureDeviceWithCA(t *testing.T) { + // we don't have a insecure device simulator for this test, so we create a fake device + // and try to onboard it + ctx, cancel := context.WithTimeout(context.Background(), time.Second*8) + defer cancel() + dev := device{Device: &core.Device{}} + links := schema.ResourceLinks{ + { + Href: cloud.ResourceURI, + ResourceTypes: []string{cloud.ResourceType}, + }, + { + Href: credential.ResourceURI, + ResourceTypes: []string{credential.ResourceType}, + }, + } + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + caPEM, err := generateCertificate.GenerateRootCA(generateCertificate.Configuration{}, key) + require.NoError(t, err) + + err = onboardInsecureDevice(ctx, &dev, links, &pb.OnboardDeviceRequest{ + DeviceId: "devId", + CoapGatewayAddress: "coaps+tcp://localhost:5684", + AuthorizationCode: "authCode", + AuthorizationProviderName: "authProviderName", + HubId: "hubId", + CertificateAuthorities: string(caPEM), + }) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "cannot add CA to credential resource")) +}