mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
NOISSUE - Fix Bootstrap thing creation flow (#2083)
Signed-off-by: Arvindh <arvindh91@gmail.com>
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/absmach/magistrala/pkg/errors"
|
||||
mgsdk "github.com/absmach/magistrala/pkg/sdk/go"
|
||||
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
|
||||
"github.com/absmach/magistrala/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -180,8 +181,8 @@ func newService() (bootstrap.Service, *authmocks.AuthClient, *sdkmocks.SDK) {
|
||||
things := mocks.NewConfigsRepository()
|
||||
auth := new(authmocks.AuthClient)
|
||||
sdk := new(sdkmocks.SDK)
|
||||
|
||||
return bootstrap.New(auth, things, sdk, encKey), auth, sdk
|
||||
idp := uuid.NewMock()
|
||||
return bootstrap.New(auth, things, sdk, encKey, idp), auth, sdk
|
||||
}
|
||||
|
||||
func newBootstrapServer(svc bootstrap.Service) *httptest.Server {
|
||||
|
||||
@@ -23,11 +23,12 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
contentType = "application/json"
|
||||
offsetKey = "offset"
|
||||
limitKey = "limit"
|
||||
defOffset = 0
|
||||
defLimit = 10
|
||||
contentType = "application/json"
|
||||
byteContentType = "application/octet-stream"
|
||||
offsetKey = "offset"
|
||||
limitKey = "limit"
|
||||
defOffset = 0
|
||||
defLimit = 10
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -259,7 +260,7 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response interface
|
||||
}
|
||||
|
||||
func encodeSecureRes(_ context.Context, w http.ResponseWriter, response interface{}) error {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.Header().Set("Content-Type", byteContentType)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if b, ok := response.([]byte); ok {
|
||||
if _, err := w.Write(b); err != nil {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/absmach/magistrala/pkg/events/store"
|
||||
mgsdk "github.com/absmach/magistrala/pkg/sdk/go"
|
||||
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
|
||||
"github.com/absmach/magistrala/pkg/uuid"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -81,8 +82,8 @@ func newService(t *testing.T, url string) (bootstrap.Service, *authmocks.AuthCli
|
||||
things := mocks.NewConfigsRepository()
|
||||
auth := new(authmocks.AuthClient)
|
||||
sdk := new(sdkmocks.SDK)
|
||||
|
||||
svc := bootstrap.New(auth, things, sdk, encKey)
|
||||
idp := uuid.NewMock()
|
||||
svc := bootstrap.New(auth, things, sdk, encKey, idp)
|
||||
publisher, err := store.NewPublisher(context.Background(), url, streamID)
|
||||
require.Nil(t, err, fmt.Sprintf("got unexpected error: %s", err))
|
||||
svc = producer.NewEventStoreMiddleware(svc, publisher)
|
||||
|
||||
+24
-25
@@ -104,19 +104,21 @@ type ConfigReader interface {
|
||||
}
|
||||
|
||||
type bootstrapService struct {
|
||||
auth magistrala.AuthServiceClient
|
||||
configs ConfigRepository
|
||||
sdk mgsdk.SDK
|
||||
encKey []byte
|
||||
auth magistrala.AuthServiceClient
|
||||
configs ConfigRepository
|
||||
sdk mgsdk.SDK
|
||||
encKey []byte
|
||||
idProvider magistrala.IDProvider
|
||||
}
|
||||
|
||||
// New returns new Bootstrap service.
|
||||
func New(auth magistrala.AuthServiceClient, configs ConfigRepository, sdk mgsdk.SDK, encKey []byte) Service {
|
||||
func New(auth magistrala.AuthServiceClient, configs ConfigRepository, sdk mgsdk.SDK, encKey []byte, idp magistrala.IDProvider) Service {
|
||||
return &bootstrapService{
|
||||
configs: configs,
|
||||
sdk: sdk,
|
||||
auth: auth,
|
||||
encKey: encKey,
|
||||
configs: configs,
|
||||
sdk: sdk,
|
||||
auth: auth,
|
||||
encKey: encKey,
|
||||
idProvider: idp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,8 +154,10 @@ func (bs bootstrapService) Add(ctx context.Context, token string, cfg Config) (C
|
||||
|
||||
saved, err := bs.configs.Save(ctx, cfg, toConnect)
|
||||
if err != nil {
|
||||
// If id is empty, then a new thing has been created function - bs.thing(id, token)
|
||||
// So, on bootstrap config save error , delete the newly created thing.
|
||||
if id == "" {
|
||||
if _, errT := bs.sdk.DisableThing(cfg.ThingID, token); errT != nil {
|
||||
if errT := bs.sdk.DeleteThing(cfg.ThingID, token); errT != nil {
|
||||
err = errors.Wrap(err, errT)
|
||||
}
|
||||
}
|
||||
@@ -381,29 +385,24 @@ func (bs bootstrapService) identify(ctx context.Context, token string) (string,
|
||||
|
||||
// Method thing retrieves Magistrala Thing creating one if an empty ID is passed.
|
||||
func (bs bootstrapService) thing(id, token string) (mgsdk.Thing, error) {
|
||||
var thing mgsdk.Thing
|
||||
var err error
|
||||
var sdkErr errors.SDKError
|
||||
|
||||
thing.ID = id
|
||||
// If Thing ID is not provided, then create new thing.
|
||||
if id == "" {
|
||||
thing, sdkErr = bs.sdk.CreateThing(mgsdk.Thing{}, token)
|
||||
id, err := bs.idProvider.ID()
|
||||
if err != nil {
|
||||
return mgsdk.Thing{}, errors.Wrap(errCreateThing, err)
|
||||
}
|
||||
thing, sdkErr := bs.sdk.CreateThing(mgsdk.Thing{ID: id, Name: "Bootstrapped Thing " + id}, token)
|
||||
if sdkErr != nil {
|
||||
return mgsdk.Thing{}, errors.Wrap(errCreateThing, errors.New(sdkErr.Err().Msg()))
|
||||
}
|
||||
return thing, nil
|
||||
}
|
||||
|
||||
thing, sdkErr = bs.sdk.Thing(thing.ID, token)
|
||||
// If Thing ID is provided, then retrieve thing
|
||||
thing, sdkErr := bs.sdk.Thing(id, token)
|
||||
if sdkErr != nil {
|
||||
err = errors.New(sdkErr.Error())
|
||||
if id != "" {
|
||||
if _, sdkErr2 := bs.sdk.DisableThing(thing.ID, token); sdkErr2 != nil {
|
||||
err = errors.Wrap(errors.New(sdkErr.Msg()), errors.New(sdkErr2.Msg()))
|
||||
}
|
||||
}
|
||||
return mgsdk.Thing{}, errors.Wrap(ErrThings, err)
|
||||
return mgsdk.Thing{}, errors.Wrap(ErrThings, sdkErr)
|
||||
}
|
||||
|
||||
return thing, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
svcerr "github.com/absmach/magistrala/pkg/errors/service"
|
||||
mgsdk "github.com/absmach/magistrala/pkg/sdk/go"
|
||||
sdkmocks "github.com/absmach/magistrala/pkg/sdk/mocks"
|
||||
"github.com/absmach/magistrala/pkg/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -60,8 +61,9 @@ func newService() (bootstrap.Service, *authmocks.AuthClient, *sdkmocks.SDK) {
|
||||
things := mocks.NewConfigsRepository()
|
||||
auth := new(authmocks.AuthClient)
|
||||
sdk := new(sdkmocks.SDK)
|
||||
idp := uuid.NewMock()
|
||||
|
||||
return bootstrap.New(auth, things, sdk, encKey), auth, sdk
|
||||
return bootstrap.New(auth, things, sdk, encKey, idp), auth, sdk
|
||||
}
|
||||
|
||||
func enc(in []byte) ([]byte, error) {
|
||||
|
||||
+2
-2
@@ -146,7 +146,7 @@ var cmdBootstrap = []cobra.Command{
|
||||
},
|
||||
},
|
||||
{
|
||||
Use: "bootstrap [<external_id> <external_key> | secure <external_id> <external_key> ]",
|
||||
Use: "bootstrap [<external_id> <external_key> | secure <external_id> <external_key> <crypto_key> ]",
|
||||
Short: "Bootstrap config",
|
||||
Long: `Returns Config to the Thing with provided external ID using external key.
|
||||
secure - Retrieves a configuration with given external ID and encrypted external key.`,
|
||||
@@ -156,7 +156,7 @@ var cmdBootstrap = []cobra.Command{
|
||||
return
|
||||
}
|
||||
if args[0] == "secure" {
|
||||
c, err := sdk.BootstrapSecure(args[1], args[2])
|
||||
c, err := sdk.BootstrapSecure(args[1], args[2], args[3])
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return
|
||||
|
||||
@@ -178,8 +178,9 @@ func newService(ctx context.Context, authClient magistrala.AuthServiceClient, db
|
||||
}
|
||||
|
||||
sdk := mgsdk.NewSDK(config)
|
||||
idp := uuid.New()
|
||||
|
||||
svc := bootstrap.New(authClient, repoConfig, sdk, []byte(cfg.EncKey))
|
||||
svc := bootstrap.New(authClient, repoConfig, sdk, []byte(cfg.EncKey), idp)
|
||||
|
||||
publisher, err := store.NewPublisher(ctx, cfg.ESURL, streamID)
|
||||
if err != nil {
|
||||
|
||||
+51
-4
@@ -4,8 +4,13 @@
|
||||
package sdk
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -233,18 +238,60 @@ func (sdk mgSDK) Bootstrap(externalID, externalKey string) (BootstrapConfig, err
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
func (sdk mgSDK) BootstrapSecure(externalID, externalKey string) (BootstrapConfig, errors.SDKError) {
|
||||
func (sdk mgSDK) BootstrapSecure(externalID, externalKey, cryptoKey string) (BootstrapConfig, errors.SDKError) {
|
||||
url := fmt.Sprintf("%s/%s/%s/%s", sdk.bootstrapURL, bootstrapEndpoint, secureEndpoint, externalID)
|
||||
|
||||
_, body, err := sdk.processRequest(http.MethodGet, url, ThingPrefix+externalKey, nil, nil, http.StatusOK)
|
||||
encExtKey, err := bootstrapEncrypt([]byte(externalKey), cryptoKey)
|
||||
if err != nil {
|
||||
return BootstrapConfig{}, err
|
||||
return BootstrapConfig{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
_, body, sdkErr := sdk.processRequest(http.MethodGet, url, ThingPrefix+encExtKey, nil, nil, http.StatusOK)
|
||||
if sdkErr != nil {
|
||||
return BootstrapConfig{}, sdkErr
|
||||
}
|
||||
|
||||
decBody, decErr := bootstrapDecrypt(body, cryptoKey)
|
||||
if decErr != nil {
|
||||
return BootstrapConfig{}, errors.NewSDKError(decErr)
|
||||
}
|
||||
var bc BootstrapConfig
|
||||
if err := json.Unmarshal(body, &bc); err != nil {
|
||||
if err := json.Unmarshal(decBody, &bc); err != nil {
|
||||
return BootstrapConfig{}, errors.NewSDKError(err)
|
||||
}
|
||||
|
||||
return bc, nil
|
||||
}
|
||||
|
||||
func bootstrapEncrypt(in []byte, cryptoKey string) (string, error) {
|
||||
block, err := aes.NewCipher([]byte(cryptoKey))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ciphertext := make([]byte, aes.BlockSize+len(in))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return "", err
|
||||
}
|
||||
stream := cipher.NewCFBEncrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext[aes.BlockSize:], in)
|
||||
return hex.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func bootstrapDecrypt(in []byte, cryptoKey string) ([]byte, error) {
|
||||
ciphertext := in
|
||||
|
||||
block, err := aes.NewCipher([]byte(cryptoKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, err
|
||||
}
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
stream := cipher.NewCFBDecrypter(block, iv)
|
||||
stream.XORKeyStream(ciphertext, ciphertext)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -916,9 +916,9 @@ type SDK interface {
|
||||
// BootstrapSecure retrieves a configuration with given external ID and encrypted external key.
|
||||
//
|
||||
// example:
|
||||
// bootstrap, _ := sdk.BootstrapSecure("externalID", "externalKey")
|
||||
// bootstrap, _ := sdk.BootstrapSecure("externalID", "externalKey", "cryptoKey")
|
||||
// fmt.Println(bootstrap)
|
||||
BootstrapSecure(externalID, externalKey string) (BootstrapConfig, errors.SDKError)
|
||||
BootstrapSecure(externalID, externalKey, cryptoKey string) (BootstrapConfig, errors.SDKError)
|
||||
|
||||
// Bootstraps retrieves a list of managed configs.
|
||||
//
|
||||
|
||||
@@ -176,9 +176,9 @@ func (_m *SDK) Bootstrap(externalID string, externalKey string) (sdk.BootstrapCo
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// BootstrapSecure provides a mock function with given fields: externalID, externalKey
|
||||
func (_m *SDK) BootstrapSecure(externalID string, externalKey string) (sdk.BootstrapConfig, errors.SDKError) {
|
||||
ret := _m.Called(externalID, externalKey)
|
||||
// BootstrapSecure provides a mock function with given fields: externalID, externalKey, cryptoKey
|
||||
func (_m *SDK) BootstrapSecure(externalID string, externalKey string, cryptoKey string) (sdk.BootstrapConfig, errors.SDKError) {
|
||||
ret := _m.Called(externalID, externalKey, cryptoKey)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for BootstrapSecure")
|
||||
@@ -186,17 +186,17 @@ func (_m *SDK) BootstrapSecure(externalID string, externalKey string) (sdk.Boots
|
||||
|
||||
var r0 sdk.BootstrapConfig
|
||||
var r1 errors.SDKError
|
||||
if rf, ok := ret.Get(0).(func(string, string) (sdk.BootstrapConfig, errors.SDKError)); ok {
|
||||
return rf(externalID, externalKey)
|
||||
if rf, ok := ret.Get(0).(func(string, string, string) (sdk.BootstrapConfig, errors.SDKError)); ok {
|
||||
return rf(externalID, externalKey, cryptoKey)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string) sdk.BootstrapConfig); ok {
|
||||
r0 = rf(externalID, externalKey)
|
||||
if rf, ok := ret.Get(0).(func(string, string, string) sdk.BootstrapConfig); ok {
|
||||
r0 = rf(externalID, externalKey, cryptoKey)
|
||||
} else {
|
||||
r0 = ret.Get(0).(sdk.BootstrapConfig)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string) errors.SDKError); ok {
|
||||
r1 = rf(externalID, externalKey)
|
||||
if rf, ok := ret.Get(1).(func(string, string, string) errors.SDKError); ok {
|
||||
r1 = rf(externalID, externalKey, cryptoKey)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(errors.SDKError)
|
||||
|
||||
Reference in New Issue
Block a user