Skip to content

Commit 5a9fcac

Browse files
committed
fix: Add unit tests for new security-proxy-auth funcs
Add unit tests for new security-proxy-auth funcs on the controller/app layers. Signed-off-by: Lindsey Cheng <beckysocute@gmail.com>
1 parent b41f93f commit 5a9fcac

File tree

14 files changed

+684
-30
lines changed

14 files changed

+684
-30
lines changed
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

6-
package utils
6+
package crypto
77

88
import (
99
"bytes"
@@ -13,6 +13,8 @@ import (
1313
"encoding/base64"
1414
"io"
1515

16+
"github.com/edgexfoundry/edgex-go/internal/pkg/utils/crypto/interfaces"
17+
1618
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
1719
)
1820

@@ -23,14 +25,14 @@ type AESCryptor struct {
2325
key []byte
2426
}
2527

26-
func NewAESCryptor() *AESCryptor {
28+
func NewAESCryptor() interfaces.Crypto {
2729
return &AESCryptor{
2830
key: []byte(aesKey),
2931
}
3032
}
3133

3234
// Encrypt encrypts the given plaintext with AES-CBC mode and returns a string in base64 encoding
33-
func (c AESCryptor) Encrypt(plaintext string) (string, errors.EdgeX) {
35+
func (c *AESCryptor) Encrypt(plaintext string) (string, errors.EdgeX) {
3436
bytePlaintext := []byte(plaintext)
3537
block, err := aes.NewCipher(c.key)
3638
if err != nil {
@@ -54,7 +56,7 @@ func (c AESCryptor) Encrypt(plaintext string) (string, errors.EdgeX) {
5456
}
5557

5658
// Decrypt decrypts the given ciphertext with AES-CBC mode and returns the original value as string
57-
func (c AESCryptor) Decrypt(ciphertext string) ([]byte, errors.EdgeX) {
59+
func (c *AESCryptor) Decrypt(ciphertext string) ([]byte, errors.EdgeX) {
5860
decodedCipherText, err := base64.StdEncoding.DecodeString(ciphertext)
5961
if err != nil {
6062
return nil, errors.NewCommonEdgeX(errors.KindServerError, "decrypt failed", err)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//
44
// SPDX-License-Identifier: Apache-2.0
55

6-
package utils
6+
package crypto
77

88
import (
99
"testing"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// Copyright (C) 2024 IOTech Ltd
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package interfaces
7+
8+
import "github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
9+
10+
type Crypto interface {
11+
Encrypt(string) (string, errors.EdgeX)
12+
Decrypt(string) ([]byte, errors.EdgeX)
13+
}

internal/pkg/utils/crypto/interfaces/mocks/Crypto.go

Lines changed: 90 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/security/proxyauth/application/key.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ package application
77

88
import (
99
"fmt"
10-
11-
"github.com/edgexfoundry/edgex-go/internal/pkg/utils"
1210
"github.com/edgexfoundry/edgex-go/internal/security/proxyauth/container"
1311
proxyAuthUtils "github.com/edgexfoundry/edgex-go/internal/security/proxyauth/utils"
14-
1512
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
1613
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
1714
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos"
@@ -23,6 +20,7 @@ import (
2320
// and then invokes AddKey function of infrastructure layer to add new user
2421
func AddKey(dic *di.Container, keyData models.KeyData) errors.EdgeX {
2522
dbClient := container.DBClientFrom(dic.Get)
23+
cryptor := container.CryptoFrom(dic.Get)
2624

2725
keyName := ""
2826
if len(keyData.Type) == 0 {
@@ -39,7 +37,7 @@ func AddKey(dic *di.Container, keyData models.KeyData) errors.EdgeX {
3937
fmt.Sprintf("key type should be one of the '%s' or '%s'", common.VerificationKeyType, common.SigningKeyType), nil)
4038
}
4139

42-
encryptedKey, err := utils.NewAESCryptor().Encrypt(keyData.Key)
40+
encryptedKey, err := cryptor.Encrypt(keyData.Key)
4341
if err != nil {
4442
return errors.NewCommonEdgeX(errors.Kind(err), "failed to encrypt the key", err)
4543
}
@@ -69,12 +67,14 @@ func VerificationKeyByIssuer(dic *di.Container, issuer string) (dtos.KeyData, er
6967
}
7068
keyName := proxyAuthUtils.VerificationKeyName(issuer)
7169
dbClient := container.DBClientFrom(dic.Get)
70+
cryptor := container.CryptoFrom(dic.Get)
7271

7372
keyData, err := dbClient.ReadKeyContent(keyName)
7473
if err != nil {
7574
return dtos.KeyData{}, errors.NewCommonEdgeXWrapper(err)
7675
}
77-
decryptedKey, err := utils.NewAESCryptor().Decrypt(keyData)
76+
77+
decryptedKey, err := cryptor.Decrypt(keyData)
7878
if err != nil {
7979
return dtos.KeyData{}, errors.NewCommonEdgeX(errors.Kind(err), "failed to decrypt the key", err)
8080
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//
2+
// Copyright (C) 2024 IOTech Ltd
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package application
7+
8+
import (
9+
"testing"
10+
11+
cryptoMocks "github.com/edgexfoundry/edgex-go/internal/pkg/utils/crypto/interfaces/mocks"
12+
"github.com/edgexfoundry/edgex-go/internal/security/proxyauth/container"
13+
"github.com/edgexfoundry/edgex-go/internal/security/proxyauth/infrastructure/interfaces/mocks"
14+
15+
bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container"
16+
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
17+
"github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger"
18+
"github.com/edgexfoundry/go-mod-core-contracts/v4/common"
19+
"github.com/edgexfoundry/go-mod-core-contracts/v4/dtos"
20+
"github.com/edgexfoundry/go-mod-core-contracts/v4/errors"
21+
"github.com/edgexfoundry/go-mod-core-contracts/v4/models"
22+
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func mockDic() *di.Container {
27+
return di.NewContainer(di.ServiceConstructorMap{
28+
bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) interface{} {
29+
return logger.NewMockClient()
30+
},
31+
})
32+
}
33+
34+
func TestAddKey(t *testing.T) {
35+
dic := mockDic()
36+
37+
validNewKey := "validNewKey"
38+
validIssuer := "testIssuer"
39+
validKeyData := models.KeyData{
40+
Type: common.VerificationKeyType,
41+
Issuer: validIssuer,
42+
Key: validNewKey,
43+
}
44+
validKeyName := validKeyData.Issuer + "/" + validKeyData.Type
45+
validEncryptedKey := "encryptedValidNewKey"
46+
47+
validUpdateKey := "validUpdateKey"
48+
updateKeyData := models.KeyData{
49+
Type: common.SigningKeyType,
50+
Issuer: "issuer2",
51+
Key: validUpdateKey,
52+
}
53+
validUpdateKeyName := updateKeyData.Issuer + "/" + updateKeyData.Type
54+
validUpdateEncryptedKey := "encryptedValidUpdateKey"
55+
56+
invalidKeyData := models.KeyData{
57+
Type: "invalidKeyType",
58+
Issuer: "issuer2",
59+
Key: validUpdateKey,
60+
}
61+
62+
encryptFailedKey := "encryptFailedKey"
63+
encryptFailedKeyData := models.KeyData{
64+
Type: common.SigningKeyType,
65+
Issuer: "issuer3",
66+
Key: encryptFailedKey,
67+
}
68+
69+
dbClientMock := &mocks.DBClient{}
70+
dbClientMock.On("KeyExists", validKeyName).Return(false, nil)
71+
dbClientMock.On("AddKey", validKeyName, validEncryptedKey).Return(nil)
72+
dbClientMock.On("KeyExists", validUpdateKeyName).Return(true, nil)
73+
dbClientMock.On("UpdateKey", validUpdateKeyName, validUpdateEncryptedKey).Return(nil)
74+
75+
cryptoMock := &cryptoMocks.Crypto{}
76+
cryptoMock.On("Encrypt", validKeyData.Key).Return(validEncryptedKey, nil)
77+
cryptoMock.On("Encrypt", updateKeyData.Key).Return(validUpdateEncryptedKey, nil)
78+
cryptoMock.On("Encrypt", encryptFailedKeyData.Key).Return("", errors.NewCommonEdgeX(errors.KindServerError, "failed to encrypt the key", nil))
79+
80+
dic.Update(di.ServiceConstructorMap{
81+
container.DBClientInterfaceName: func(get di.Get) interface{} {
82+
return dbClientMock
83+
},
84+
container.CryptoInterfaceName: func(get di.Get) interface{} {
85+
return cryptoMock
86+
},
87+
})
88+
89+
tests := []struct {
90+
name string
91+
keyData models.KeyData
92+
errorExpected bool
93+
errKind errors.ErrKind
94+
}{
95+
{"Valid - Add new verification key", validKeyData, false, ""},
96+
{"Valid - Update existing signing key", updateKeyData, false, ""},
97+
{"Invalid - Invalid key type", invalidKeyData, true, errors.KindContractInvalid},
98+
{"Invalid - Encryption Error", encryptFailedKeyData, true, errors.KindServerError},
99+
}
100+
101+
for _, test := range tests {
102+
t.Run(test.name, func(t *testing.T) {
103+
err := AddKey(dic, test.keyData)
104+
if test.errorExpected {
105+
require.Error(t, err)
106+
require.Equal(t, test.errKind, errors.Kind(err))
107+
} else {
108+
require.NoError(t, err)
109+
}
110+
})
111+
}
112+
}
113+
114+
func TestVerificationKeyByIssuer(t *testing.T) {
115+
dic := mockDic()
116+
117+
validIssuer := "issuer1"
118+
validEncryptedKey := "encryptedKey"
119+
expectedKeyName := validIssuer + "/" + common.VerificationKeyType
120+
expectedKeyData := dtos.KeyData{Issuer: validIssuer, Type: common.VerificationKeyType, Key: "decryptedKey"}
121+
122+
invalidIssuer := "invalidIssuer"
123+
invalidKeyName := invalidIssuer + "/" + common.VerificationKeyType
124+
125+
decryptErrIssuer := "decryptErrIssuer"
126+
decryptErrKeyName := decryptErrIssuer + "/" + common.VerificationKeyType
127+
decryptErrKey := "decryptErrKey"
128+
129+
dbClientMock := &mocks.DBClient{}
130+
dbClientMock.On("ReadKeyContent", expectedKeyName).Return(validEncryptedKey, nil)
131+
dbClientMock.On("ReadKeyContent", invalidKeyName).Return("", errors.NewCommonEdgeX(errors.KindServerError, "read key error", nil))
132+
dbClientMock.On("ReadKeyContent", decryptErrKeyName).Return(decryptErrKey, nil)
133+
134+
cryptoMock := &cryptoMocks.Crypto{}
135+
cryptoMock.On("Decrypt", validEncryptedKey).Return([]byte("decryptedKey"), nil)
136+
cryptoMock.On("Decrypt", decryptErrKey).Return([]byte{}, errors.NewCommonEdgeX(errors.KindServerError, "decrypt key error", nil))
137+
138+
dic.Update(di.ServiceConstructorMap{
139+
container.DBClientInterfaceName: func(get di.Get) interface{} {
140+
return dbClientMock
141+
},
142+
container.CryptoInterfaceName: func(get di.Get) interface{} {
143+
return cryptoMock
144+
},
145+
})
146+
147+
tests := []struct {
148+
name string
149+
issuer string
150+
expectedKeyData dtos.KeyData
151+
errorExpected bool
152+
errKind errors.ErrKind
153+
}{
154+
{"Valid - Valid key", validIssuer, expectedKeyData, false, ""},
155+
{"Invalid - Empty issuer", "", dtos.KeyData{}, true, errors.KindContractInvalid},
156+
{"Invalid - Key read error", invalidIssuer, dtos.KeyData{}, true, errors.KindServerError},
157+
{"Invalid - Decryption error", decryptErrIssuer, dtos.KeyData{}, true, errors.KindServerError},
158+
}
159+
160+
for _, test := range tests {
161+
t.Run(test.name, func(t *testing.T) {
162+
result, err := VerificationKeyByIssuer(dic, test.issuer)
163+
if test.errorExpected {
164+
require.Error(t, err)
165+
require.Equal(t, test.errKind, errors.Kind(err))
166+
} else {
167+
require.NoError(t, err)
168+
require.Equal(t, test.expectedKeyData, result)
169+
}
170+
})
171+
}
172+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Copyright (C) 2024 IOTech Ltd
3+
//
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
package container
7+
8+
import (
9+
"github.com/edgexfoundry/edgex-go/internal/pkg/utils/crypto/interfaces"
10+
11+
"github.com/edgexfoundry/go-mod-bootstrap/v4/di"
12+
)
13+
14+
// CryptoInterfaceName contains the name of the interfaces.Crypto implementation in the DIC.
15+
var CryptoInterfaceName = di.TypeInstanceToName((*interfaces.Crypto)(nil))
16+
17+
// CryptoFrom helper function queries the DIC and returns the interfaces.Cryptor implementation.
18+
func CryptoFrom(get di.Get) interfaces.Crypto {
19+
return get(CryptoInterfaceName).(interfaces.Crypto)
20+
}

0 commit comments

Comments
 (0)