Skip to content

Commit bd248e2

Browse files
committed
simplified unified TLS config
1 parent 8cc73d9 commit bd248e2

File tree

9 files changed

+524
-100
lines changed

9 files changed

+524
-100
lines changed

internal/config/config.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,18 @@ func GetConfig(cmd *cobra.Command, configFile string) (Config, Meta, error) {
151151
v := viper.NewWithOptions(viper.WithDecodeHook(mapstructure.ComposeDecodeHookFunc(
152152
mapstructure.StringToTimeDurationHookFunc(),
153153
configtypes.StringToDurationHookFunc(),
154+
configtypes.StringToPEMDataHookFunc(),
154155
)))
155156

156157
if cmd != nil {
157158
bindPFlags := []string{
158-
"port", "address", "internal_port", "internal_address", "log_level", "log_file", "pid_file",
159-
"engine.type", "broker.enabled", "broker.type", "presence_manager.enabled", "presence_manager.type",
160-
"debug.enabled", "admin.enabled", "admin.external", "admin.insecure", "client.insecure", "http_api.insecure",
161-
"http_api.external", "prometheus.enabled", "health.enabled", "grpc_api.enabled", "grpc_api.port",
162-
"uni_grpc.enabled", "uni_grpc.port", "uni_websocket.enabled", "uni_sse.enabled", "uni_http_stream.enabled",
163-
"sse.enabled", "http_stream.enabled", "swagger.enabled",
159+
"pid_file", "http_server.port", "http_server.address", "http_server.internal_port",
160+
"http_server.internal_address", "log.level", "log.file", "engine.type", "broker.enabled", "broker.type",
161+
"presence_manager.enabled", "presence_manager.type", "debug.enabled", "admin.enabled", "admin.external",
162+
"admin.insecure", "client.insecure", "http_api.insecure", "http_api.external", "prometheus.enabled",
163+
"health.enabled", "grpc_api.enabled", "grpc_api.port", "uni_grpc.enabled", "uni_grpc.port",
164+
"uni_websocket.enabled", "uni_sse.enabled", "uni_http_stream.enabled", "sse.enabled", "http_stream.enabled",
165+
"swagger.enabled",
164166
}
165167
for _, flag := range bindPFlags {
166168
_ = v.BindPFlag(flag, cmd.Flags().Lookup(flag))

internal/config/config_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ func getConfig(t *testing.T, configFile string) (Config, Meta) {
2020
func checkConfig(t *testing.T, conf Config) {
2121
t.Helper()
2222
require.NotNil(t, conf)
23+
require.True(t, conf.HTTP.TLS.Enabled)
24+
require.NotZero(t, conf.HTTP.TLS.ServerCAPem)
25+
_, err := conf.HTTP.TLS.ToGoTLSConfig("test")
26+
require.NoError(t, err)
2327
require.Equal(t, "https://example.com/jwks", conf.Client.Token.JWKSPublicEndpoint)
2428
require.Len(t, conf.Client.AllowedOrigins, 1)
2529
require.Equal(t, "http://localhost:3000", conf.Client.AllowedOrigins[0])

internal/config/testdata/config.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"http_server": {
33
"tls": {
4-
"enabled": true
4+
"enabled": true,
5+
"server_ca_pem": "-----BEGIN CERTIFICATE-----\nMIIEbjCCAtagAwIBAgIRAN1ZJEYl5ZNIOHsbJizQpucwDQYJKoZIhvcNAQELBQAw\ngZ8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE6MDgGA1UECwwxZnpA\nTWFjQm9vay1Qcm8tQWxleGFuZGVyLmxvY2FsIChBbGV4YW5kZXIgRW1lbGluKTFB\nMD8GA1UEAww4bWtjZXJ0IGZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAo\nQWxleGFuZGVyIEVtZWxpbikwHhcNMjIwNjE2MDYxOTM0WhcNMjQwOTE2MDYxOTM0\nWjBlMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxOjA4\nBgNVBAsMMWZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAoQWxleGFuZGVy\nIEVtZWxpbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLCNVIle5k\nlfRtzjHe9sEo8zU9pqXfK9fxc2PZqfd6HVDVWyrOHNv9zWV8awEEgwX2kg+sY4ch\nuKmNdD19UWxLovCMkA92gKhzJoPPBMlVRtSA9QWNw4cXXB25KErPPyBXyyFA13X/\n6N408I26Aj6ewA0WLISkNgiCddUo31FygTNH4yWXF+F+lol0EJhG+K3E8diYub4P\n1Ul417sQ/1FxcoGo43fGl8j4y6wCnBQkSNaQCr1vvNEzdmiIYF02a51Efdb3PrSu\n90nJJBbFQxNhpcl98tLRF5t3wZJ+R2Xy4xPUZYwNNWTdICqW7a4bfD4foByp85kr\nu44kw7laXghhAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr\nBgEFBQcDATAfBgNVHSMEGDAWgBSMh55IrbevJTB4kiFUXsarEAIjXjAUBgNVHREE\nDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBAG9yTMOybS6ike/UoIrj\nxLE3a9nPuFdt9anS0XgYicCYNFLc5H6MUXubsqBz30zigFbNP/FR2DKvIP+1cySP\nDKqnimTdxZWjzT9d0YHEYcD971yk/whXmKOcla2VmYMuPmUr6M3BmUmYcoWve/ML\nnc8qKJ+CsM80zxFSRbqCVqgPfNDzPHqGbJmOn0KbLPWzkUsIbii/O4IjqycJiDMS\nCyuat2Q8TYGiRhDJnouD/semDtqaIGGT77/5QLoEhFRwRKbOfgTT0hjLgTbeKPrx\nQKARxjVC/QF59nhdf+je/BgrF7jfR1UuCSxwl0xg2Ub2JB5A77efWEoQh2fuSgZk\nmVTZqDnfGvfYcGE9oiAMl21DimEAdYFSAUTtVI6T0S8BagN3jD+FLV7+TJgPiyIO\nLz9gcDP1Zn3jIp4Vy2HawWt+8rta351L70ie9Sk6Cx5fV0slvTFteWYdm26BuKbp\nNF7OqlGSRzM2iEVaMFLqnrRwDF4bR7qwGukppEXPrsAq2Q==\n-----END CERTIFICATE-----"
56
}
67
},
78
"engine": {

internal/config/testdata/config.toml

+28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
[http_server.tls]
44
enabled = true
5+
server_ca_pem = """
6+
-----BEGIN CERTIFICATE-----
7+
MIIEbjCCAtagAwIBAgIRAN1ZJEYl5ZNIOHsbJizQpucwDQYJKoZIhvcNAQELBQAw
8+
gZ8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE6MDgGA1UECwwxZnpA
9+
TWFjQm9vay1Qcm8tQWxleGFuZGVyLmxvY2FsIChBbGV4YW5kZXIgRW1lbGluKTFB
10+
MD8GA1UEAww4bWtjZXJ0IGZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAo
11+
QWxleGFuZGVyIEVtZWxpbikwHhcNMjIwNjE2MDYxOTM0WhcNMjQwOTE2MDYxOTM0
12+
WjBlMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxOjA4
13+
BgNVBAsMMWZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAoQWxleGFuZGVy
14+
IEVtZWxpbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLCNVIle5k
15+
lfRtzjHe9sEo8zU9pqXfK9fxc2PZqfd6HVDVWyrOHNv9zWV8awEEgwX2kg+sY4ch
16+
uKmNdD19UWxLovCMkA92gKhzJoPPBMlVRtSA9QWNw4cXXB25KErPPyBXyyFA13X/
17+
6N408I26Aj6ewA0WLISkNgiCddUo31FygTNH4yWXF+F+lol0EJhG+K3E8diYub4P
18+
1Ul417sQ/1FxcoGo43fGl8j4y6wCnBQkSNaQCr1vvNEzdmiIYF02a51Efdb3PrSu
19+
90nJJBbFQxNhpcl98tLRF5t3wZJ+R2Xy4xPUZYwNNWTdICqW7a4bfD4foByp85kr
20+
u44kw7laXghhAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
21+
BgEFBQcDATAfBgNVHSMEGDAWgBSMh55IrbevJTB4kiFUXsarEAIjXjAUBgNVHREE
22+
DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBAG9yTMOybS6ike/UoIrj
23+
xLE3a9nPuFdt9anS0XgYicCYNFLc5H6MUXubsqBz30zigFbNP/FR2DKvIP+1cySP
24+
DKqnimTdxZWjzT9d0YHEYcD971yk/whXmKOcla2VmYMuPmUr6M3BmUmYcoWve/ML
25+
nc8qKJ+CsM80zxFSRbqCVqgPfNDzPHqGbJmOn0KbLPWzkUsIbii/O4IjqycJiDMS
26+
Cyuat2Q8TYGiRhDJnouD/semDtqaIGGT77/5QLoEhFRwRKbOfgTT0hjLgTbeKPrx
27+
QKARxjVC/QF59nhdf+je/BgrF7jfR1UuCSxwl0xg2Ub2JB5A77efWEoQh2fuSgZk
28+
mVTZqDnfGvfYcGE9oiAMl21DimEAdYFSAUTtVI6T0S8BagN3jD+FLV7+TJgPiyIO
29+
Lz9gcDP1Zn3jIp4Vy2HawWt+8rta351L70ie9Sk6Cx5fV0slvTFteWYdm26BuKbp
30+
NF7OqlGSRzM2iEVaMFLqnrRwDF4bR7qwGukppEXPrsAq2Q==
31+
-----END CERTIFICATE-----
32+
"""
533

634
[engine]
735
type = "redis"

internal/config/testdata/config.yaml

+27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,33 @@
22
http_server:
33
tls:
44
enabled: true
5+
server_ca_pem: |
6+
-----BEGIN CERTIFICATE-----
7+
MIIEbjCCAtagAwIBAgIRAN1ZJEYl5ZNIOHsbJizQpucwDQYJKoZIhvcNAQELBQAw
8+
gZ8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE6MDgGA1UECwwxZnpA
9+
TWFjQm9vay1Qcm8tQWxleGFuZGVyLmxvY2FsIChBbGV4YW5kZXIgRW1lbGluKTFB
10+
MD8GA1UEAww4bWtjZXJ0IGZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAo
11+
QWxleGFuZGVyIEVtZWxpbikwHhcNMjIwNjE2MDYxOTM0WhcNMjQwOTE2MDYxOTM0
12+
WjBlMScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxOjA4
13+
BgNVBAsMMWZ6QE1hY0Jvb2stUHJvLUFsZXhhbmRlci5sb2NhbCAoQWxleGFuZGVy
14+
IEVtZWxpbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLCNVIle5k
15+
lfRtzjHe9sEo8zU9pqXfK9fxc2PZqfd6HVDVWyrOHNv9zWV8awEEgwX2kg+sY4ch
16+
uKmNdD19UWxLovCMkA92gKhzJoPPBMlVRtSA9QWNw4cXXB25KErPPyBXyyFA13X/
17+
6N408I26Aj6ewA0WLISkNgiCddUo31FygTNH4yWXF+F+lol0EJhG+K3E8diYub4P
18+
1Ul417sQ/1FxcoGo43fGl8j4y6wCnBQkSNaQCr1vvNEzdmiIYF02a51Efdb3PrSu
19+
90nJJBbFQxNhpcl98tLRF5t3wZJ+R2Xy4xPUZYwNNWTdICqW7a4bfD4foByp85kr
20+
u44kw7laXghhAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
21+
BgEFBQcDATAfBgNVHSMEGDAWgBSMh55IrbevJTB4kiFUXsarEAIjXjAUBgNVHREE
22+
DTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGBAG9yTMOybS6ike/UoIrj
23+
xLE3a9nPuFdt9anS0XgYicCYNFLc5H6MUXubsqBz30zigFbNP/FR2DKvIP+1cySP
24+
DKqnimTdxZWjzT9d0YHEYcD971yk/whXmKOcla2VmYMuPmUr6M3BmUmYcoWve/ML
25+
nc8qKJ+CsM80zxFSRbqCVqgPfNDzPHqGbJmOn0KbLPWzkUsIbii/O4IjqycJiDMS
26+
Cyuat2Q8TYGiRhDJnouD/semDtqaIGGT77/5QLoEhFRwRKbOfgTT0hjLgTbeKPrx
27+
QKARxjVC/QF59nhdf+je/BgrF7jfR1UuCSxwl0xg2Ub2JB5A77efWEoQh2fuSgZk
28+
mVTZqDnfGvfYcGE9oiAMl21DimEAdYFSAUTtVI6T0S8BagN3jD+FLV7+TJgPiyIO
29+
Lz9gcDP1Zn3jIp4Vy2HawWt+8rta351L70ie9Sk6Cx5fV0slvTFteWYdm26BuKbp
30+
NF7OqlGSRzM2iEVaMFLqnrRwDF4bR7qwGukppEXPrsAq2Q==
31+
-----END CERTIFICATE-----
532
engine:
633
type: redis
734
redis:

internal/configtypes/pem.go

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package configtypes
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
"encoding/pem"
7+
"errors"
8+
"fmt"
9+
"reflect"
10+
11+
"github.com/go-viper/mapstructure/v2"
12+
)
13+
14+
// PEMData represents a flexible PEM-encoded source.
15+
// The order sources checked is the following:
16+
// 1. Raw PEM content
17+
// 2. Base64 encoded PEM content
18+
// 3. Path to file with PEM content
19+
type PEMData string
20+
21+
// String converts PEMData to a string.
22+
func (p PEMData) String() string {
23+
return string(p)
24+
}
25+
26+
// MarshalJSON converts PEMData to JSON.
27+
func (p PEMData) MarshalJSON() ([]byte, error) {
28+
return json.Marshal(string(p))
29+
}
30+
31+
// UnmarshalJSON parses PEMData from JSON.
32+
func (p *PEMData) UnmarshalJSON(data []byte) error {
33+
var str string
34+
if err := json.Unmarshal(data, &str); err != nil {
35+
return err
36+
}
37+
*p = PEMData(str)
38+
return nil
39+
}
40+
41+
// MarshalText converts PEMData to text for TOML.
42+
func (p PEMData) MarshalText() ([]byte, error) {
43+
return []byte(p.String()), nil
44+
}
45+
46+
// UnmarshalText parses PEMData from text (used in TOML).
47+
func (p *PEMData) UnmarshalText(text []byte) error {
48+
*p = PEMData(text)
49+
return nil
50+
}
51+
52+
// MarshalYAML converts PEMData to a YAML-compatible format.
53+
func (p PEMData) MarshalYAML() (interface{}, error) {
54+
return p.String(), nil
55+
}
56+
57+
// UnmarshalYAML parses PEMData from YAML.
58+
func (p *PEMData) UnmarshalYAML(unmarshal func(interface{}) error) error {
59+
var str string
60+
if err := unmarshal(&str); err != nil {
61+
return err
62+
}
63+
*p = PEMData(str)
64+
return nil
65+
}
66+
67+
// StringToPEMDataHookFunc for mapstructure to decode PEMData from strings.
68+
func StringToPEMDataHookFunc() mapstructure.DecodeHookFunc {
69+
return func(
70+
f reflect.Type,
71+
t reflect.Type,
72+
data interface{},
73+
) (interface{}, error) {
74+
if f.Kind() != reflect.String {
75+
return data, nil
76+
}
77+
if t != reflect.TypeOf(PEMData("")) {
78+
return data, nil
79+
}
80+
81+
return PEMData(data.(string)), nil
82+
}
83+
}
84+
85+
// isValidPEM validates if the input string is a valid PEM block.
86+
func isValidPEM(pemData string) bool {
87+
// Decode the PEM data
88+
block, _ := pem.Decode([]byte(pemData))
89+
return block != nil
90+
}
91+
92+
// Load detects if PEMData is a file path, base64 string, or raw PEM string and loads the content.
93+
func (p PEMData) Load(statFile StatFileFunc, readFile ReadFileFunc) ([]byte, string, error) {
94+
value := string(p)
95+
if isValidPEM(value) {
96+
return []byte(value), "raw pem", nil
97+
}
98+
// Check if it's base64 encoded.
99+
if decodedValue, err := base64.StdEncoding.DecodeString(value); err == nil {
100+
if isValidPEM(string(decodedValue)) {
101+
return decodedValue, "base64 pem", nil
102+
}
103+
}
104+
// Check if it's a file path by verifying if the file exists.
105+
if _, err := statFile(value); err == nil {
106+
content, err := readFile(value)
107+
if err != nil {
108+
return nil, "", fmt.Errorf("error reading file: %w", err)
109+
}
110+
if !isValidPEM(string(content)) {
111+
return nil, "", fmt.Errorf("file \"%s\" contains invalid PEM data", value)
112+
}
113+
return content, "pem file path", nil
114+
}
115+
return nil, "", errors.New("invalid PEM data: not a valid file path, base64-encoded PEM content, or raw PEM content")
116+
}

internal/configtypes/pem_test.go

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package configtypes
2+
3+
import (
4+
"encoding/base64"
5+
"os"
6+
"reflect"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestPEMData_Load(t *testing.T) {
15+
mockStatFile := func(name string) (os.FileInfo, error) {
16+
if name != "test-file" {
17+
return nil, os.ErrNotExist
18+
}
19+
return nil, nil
20+
}
21+
mockReadFile := func(name string) ([]byte, error) {
22+
if name != "test-file" {
23+
return nil, os.ErrNotExist
24+
}
25+
return []byte(testPEMCert), nil
26+
}
27+
28+
tests := []struct {
29+
name string
30+
data PEMData
31+
expected string
32+
}{
33+
{"File Path", PEMData("test-file"), testPEMCert},
34+
{"Base64", PEMData(base64.StdEncoding.EncodeToString([]byte(testPEMCert))), testPEMCert},
35+
{"Raw PEM", PEMData(testPEMCert), testPEMCert},
36+
}
37+
38+
for _, tt := range tests {
39+
t.Run(tt.name, func(t *testing.T) {
40+
data, _, err := tt.data.Load(mockStatFile, mockReadFile)
41+
require.NoError(t, err)
42+
require.Equal(t, tt.expected, string(data))
43+
})
44+
}
45+
}
46+
47+
func TestStringToPEMDataHookFunc(t *testing.T) {
48+
hook := StringToPEMDataHookFunc().(func(
49+
f reflect.Type,
50+
t reflect.Type,
51+
data interface{},
52+
) (interface{}, error))
53+
result, err := hook(reflect.TypeOf(""), reflect.TypeOf(PEMData("")), "test-data")
54+
require.NoError(t, err)
55+
require.Equal(t, PEMData("test-data"), result)
56+
}
57+
58+
const testPEMCert = `
59+
-- GlobalSign Root R2, valid until Dec 15, 2021
60+
-----BEGIN CERTIFICATE-----
61+
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
62+
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
63+
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
64+
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
65+
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
66+
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
67+
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
68+
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
69+
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
70+
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
71+
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
72+
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
73+
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
74+
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
75+
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
76+
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
77+
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
78+
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
79+
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
80+
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
81+
-----END CERTIFICATE-----`
82+
83+
func Test_isValidPEM(t *testing.T) {
84+
tests := []struct {
85+
name string
86+
data string
87+
expected bool
88+
}{
89+
{"Valid PEM", testPEMCert, true},
90+
{"Invalid PEM", "/etc/passwd", false},
91+
}
92+
for _, tt := range tests {
93+
t.Run(tt.name, func(t *testing.T) {
94+
result := isValidPEM(tt.data)
95+
require.Equal(t, tt.expected, result)
96+
})
97+
}
98+
}
99+
100+
const invalidPEM = "invalid PEM content"
101+
102+
func TestPEMData_Load_Invalid(t *testing.T) {
103+
pem := PEMData(invalidPEM)
104+
105+
_, _, err := pem.Load(mockStatFileSuccess, mockReadFileInvalid)
106+
require.Error(t, err)
107+
assert.Contains(t, err.Error(), "invalid PEM data")
108+
}

0 commit comments

Comments
 (0)