snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
This commit is contained in:
116
internal/configtypes/pem.go
Normal file
116
internal/configtypes/pem.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package configtypes
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
)
|
||||
|
||||
// PEMData represents a flexible PEM-encoded source.
|
||||
// The order sources checked is the following:
|
||||
// 1. Raw PEM content
|
||||
// 2. Base64 encoded PEM content
|
||||
// 3. Path to file with PEM content
|
||||
type PEMData string
|
||||
|
||||
// String converts PEMData to a string.
|
||||
func (p PEMData) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
// MarshalJSON converts PEMData to JSON.
|
||||
func (p PEMData) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(string(p))
|
||||
}
|
||||
|
||||
// UnmarshalJSON parses PEMData from JSON.
|
||||
func (p *PEMData) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err != nil {
|
||||
return err
|
||||
}
|
||||
*p = PEMData(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText converts PEMData to text for TOML.
|
||||
func (p PEMData) MarshalText() ([]byte, error) {
|
||||
return []byte(p.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalText parses PEMData from text (used in TOML).
|
||||
func (p *PEMData) UnmarshalText(text []byte) error {
|
||||
*p = PEMData(text)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML converts PEMData to a YAML-compatible format.
|
||||
func (p PEMData) MarshalYAML() (interface{}, error) {
|
||||
return p.String(), nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML parses PEMData from YAML.
|
||||
func (p *PEMData) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var str string
|
||||
if err := unmarshal(&str); err != nil {
|
||||
return err
|
||||
}
|
||||
*p = PEMData(str)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringToPEMDataHookFunc for mapstructure to decode PEMData from strings.
|
||||
func StringToPEMDataHookFunc() mapstructure.DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{},
|
||||
) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(PEMData("")) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return PEMData(data.(string)), nil
|
||||
}
|
||||
}
|
||||
|
||||
// isValidPEM validates if the input string is a valid PEM block.
|
||||
func isValidPEM(pemData string) bool {
|
||||
// Decode the PEM data
|
||||
block, _ := pem.Decode([]byte(pemData))
|
||||
return block != nil
|
||||
}
|
||||
|
||||
// Load detects if PEMData is a file path, base64 string, or raw PEM string and loads the content.
|
||||
func (p PEMData) Load(statFile StatFileFunc, readFile ReadFileFunc) ([]byte, string, error) {
|
||||
value := string(p)
|
||||
if isValidPEM(value) {
|
||||
return []byte(value), "raw pem", nil
|
||||
}
|
||||
// Check if it's base64 encoded.
|
||||
if decodedValue, err := base64.StdEncoding.DecodeString(value); err == nil {
|
||||
if isValidPEM(string(decodedValue)) {
|
||||
return decodedValue, "base64 pem", nil
|
||||
}
|
||||
}
|
||||
// Check if it's a file path by verifying if the file exists.
|
||||
if _, err := statFile(value); err == nil {
|
||||
content, err := readFile(value)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("error reading file: %w", err)
|
||||
}
|
||||
if !isValidPEM(string(content)) {
|
||||
return nil, "", fmt.Errorf("file \"%s\" contains invalid PEM data", value)
|
||||
}
|
||||
return content, "pem file path", nil
|
||||
}
|
||||
return nil, "", errors.New("invalid PEM data: not a valid file path, base64-encoded PEM content, or raw PEM content")
|
||||
}
|
||||
Reference in New Issue
Block a user