helpers.go raw

   1  // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates.  All rights reserved.
   2  // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
   3  
   4  //lint:file-ignore SA1019 older versions of staticcheck (those compatible with Golang 1.17) falsely flag x509.IsEncryptedPEMBlock and x509.DecryptPEMBlock.
   5  
   6  package common
   7  
   8  import (
   9  	"crypto/rand"
  10  	"crypto/rsa"
  11  	"crypto/x509"
  12  	"encoding/pem"
  13  	"errors"
  14  	"fmt"
  15  	"net/textproto"
  16  	"os"
  17  	"reflect"
  18  	"strconv"
  19  	"strings"
  20  	"time"
  21  
  22  	"github.com/youmark/pkcs8"
  23  )
  24  
  25  // String returns a pointer to the provided string
  26  func String(value string) *string {
  27  	return &value
  28  }
  29  
  30  // Int returns a pointer to the provided int
  31  func Int(value int) *int {
  32  	return &value
  33  }
  34  
  35  // Int64 returns a pointer to the provided int64
  36  func Int64(value int64) *int64 {
  37  	return &value
  38  }
  39  
  40  // Uint returns a pointer to the provided uint
  41  func Uint(value uint) *uint {
  42  	return &value
  43  }
  44  
  45  // Float32 returns a pointer to the provided float32
  46  func Float32(value float32) *float32 {
  47  	return &value
  48  }
  49  
  50  // Float64 returns a pointer to the provided float64
  51  func Float64(value float64) *float64 {
  52  	return &value
  53  }
  54  
  55  // Bool returns a pointer to the provided bool
  56  func Bool(value bool) *bool {
  57  	return &value
  58  }
  59  
  60  // PointerString prints the values of pointers in a struct
  61  // Producing a human friendly string for an struct with pointers.
  62  // useful when debugging the values of a struct
  63  func PointerString(datastruct interface{}) (representation string) {
  64  	val := reflect.ValueOf(datastruct)
  65  	typ := reflect.TypeOf(datastruct)
  66  	all := make([]string, 2)
  67  	all = append(all, "{")
  68  	for i := 0; i < typ.NumField(); i++ {
  69  		sf := typ.Field(i)
  70  
  71  		//unexported
  72  		if sf.PkgPath != "" && !sf.Anonymous {
  73  			continue
  74  		}
  75  
  76  		sv := val.Field(i)
  77  		stringValue := ""
  78  		if isNil(sv) {
  79  			stringValue = fmt.Sprintf("%s=<nil>", sf.Name)
  80  		} else {
  81  			if sv.Type().Kind() == reflect.Ptr {
  82  				sv = sv.Elem()
  83  			}
  84  			stringValue = fmt.Sprintf("%s=%v", sf.Name, sv)
  85  		}
  86  		all = append(all, stringValue)
  87  	}
  88  	all = append(all, "}")
  89  	representation = strings.TrimSpace(strings.Join(all, " "))
  90  	return
  91  }
  92  
  93  // SDKTime a struct that parses/renders to/from json using RFC339 date-time information
  94  type SDKTime struct {
  95  	time.Time
  96  }
  97  
  98  // SDKDate a struct that parses/renders to/from json using only date information
  99  type SDKDate struct {
 100  	//Date date information
 101  	Date time.Time
 102  }
 103  
 104  func sdkTimeFromTime(t time.Time) SDKTime {
 105  	return SDKTime{t}
 106  }
 107  
 108  func sdkDateFromTime(t time.Time) SDKDate {
 109  	return SDKDate{Date: t}
 110  }
 111  
 112  func formatTime(t SDKTime) string {
 113  	return t.Format(sdkTimeFormat)
 114  }
 115  
 116  func formatDate(t SDKDate) string {
 117  	return t.Date.Format(sdkDateFormat)
 118  }
 119  
 120  func now() *SDKTime {
 121  	t := SDKTime{time.Now()}
 122  	return &t
 123  }
 124  
 125  var timeType = reflect.TypeOf(SDKTime{})
 126  var timeTypePtr = reflect.TypeOf(&SDKTime{})
 127  
 128  var sdkDateType = reflect.TypeOf(SDKDate{})
 129  var sdkDateTypePtr = reflect.TypeOf(&SDKDate{})
 130  
 131  // Formats for sdk supported time representations
 132  const sdkTimeFormat = time.RFC3339Nano
 133  const rfc1123OptionalLeadingDigitsInDay = "Mon, _2 Jan 2006 15:04:05 MST"
 134  const sdkDateFormat = "2006-01-02"
 135  
 136  func tryParsingTimeWithValidFormatsForHeaders(data []byte, headerName string) (t time.Time, err error) {
 137  	header := strings.ToLower(headerName)
 138  	switch header {
 139  	case "lastmodified", "date":
 140  		t, err = tryParsing(data, time.RFC3339Nano, time.RFC3339, time.RFC1123, rfc1123OptionalLeadingDigitsInDay, time.RFC850, time.ANSIC)
 141  		return
 142  	default: //By default we parse with RFC3339
 143  		t, err = time.Parse(sdkTimeFormat, string(data))
 144  		return
 145  	}
 146  }
 147  
 148  func tryParsing(data []byte, layouts ...string) (tm time.Time, err error) {
 149  	datestring := string(data)
 150  	for _, l := range layouts {
 151  		tm, err = time.Parse(l, datestring)
 152  		if err == nil {
 153  			return
 154  		}
 155  	}
 156  	err = fmt.Errorf("could not parse time: %s with formats: %s", datestring, layouts[:])
 157  	return
 158  }
 159  
 160  // String returns string representation of SDKDate
 161  func (t *SDKDate) String() string {
 162  	return t.Date.Format(sdkDateFormat)
 163  }
 164  
 165  // NewSDKDateFromString parses the dateString into SDKDate
 166  func NewSDKDateFromString(dateString string) (*SDKDate, error) {
 167  	parsedTime, err := time.Parse(sdkDateFormat, dateString)
 168  	if err != nil {
 169  		return nil, err
 170  	}
 171  
 172  	return &SDKDate{Date: parsedTime}, nil
 173  }
 174  
 175  // UnmarshalJSON unmarshals from json
 176  func (t *SDKTime) UnmarshalJSON(data []byte) (e error) {
 177  	s := string(data)
 178  	if s == "null" {
 179  		t.Time = time.Time{}
 180  	} else {
 181  		//Try parsing with RFC3339
 182  		t.Time, e = time.Parse(`"`+sdkTimeFormat+`"`, string(data))
 183  	}
 184  	return
 185  }
 186  
 187  // MarshalJSON marshals to JSON
 188  func (t *SDKTime) MarshalJSON() (buff []byte, e error) {
 189  	s := t.Format(sdkTimeFormat)
 190  	buff = []byte(`"` + s + `"`)
 191  	return
 192  }
 193  
 194  // UnmarshalJSON unmarshals from json
 195  func (t *SDKDate) UnmarshalJSON(data []byte) (e error) {
 196  	if string(data) == `"null"` {
 197  		t.Date = time.Time{}
 198  		return
 199  	}
 200  
 201  	t.Date, e = tryParsing(data,
 202  		strconv.Quote(sdkDateFormat),
 203  	)
 204  	return
 205  }
 206  
 207  // MarshalJSON marshals to JSON
 208  func (t *SDKDate) MarshalJSON() (buff []byte, e error) {
 209  	s := t.Date.Format(sdkDateFormat)
 210  	buff = []byte(strconv.Quote(s))
 211  	return
 212  }
 213  
 214  // PrivateKeyFromBytes is a helper function that will produce a RSA private
 215  // key from bytes. This function is deprecated in favour of PrivateKeyFromBytesWithPassword
 216  // Deprecated
 217  func PrivateKeyFromBytes(pemData []byte, password *string) (key *rsa.PrivateKey, e error) {
 218  	if password == nil {
 219  		return PrivateKeyFromBytesWithPassword(pemData, nil)
 220  	}
 221  
 222  	return PrivateKeyFromBytesWithPassword(pemData, []byte(*password))
 223  }
 224  
 225  // PrivateKeyFromBytesWithPassword is a helper function that will produce a RSA private
 226  // key from bytes and a password.
 227  func PrivateKeyFromBytesWithPassword(pemData, password []byte) (key *rsa.PrivateKey, e error) {
 228  	pemBlock, _ := pem.Decode(pemData)
 229  	if pemBlock == nil {
 230  		e = fmt.Errorf("PEM data was not found in buffer")
 231  		return
 232  	}
 233  
 234  	decrypted := pemBlock.Bytes
 235  	// Support for encrypted PKCS8 format, this format can not be handled by x509.IsEncryptedPEMBlock func
 236  	if key, e = pkcs8.ParsePKCS8PrivateKeyRSA(pemBlock.Bytes, password); key != nil {
 237  		return
 238  	}
 239  	// if pemBlock.Type == "ENCRYPTED PRIVATE KEY" {
 240  	// 	return pkcs8.ParsePKCS8PrivateKeyRSA(pemData, password)
 241  	// }
 242  	if x509.IsEncryptedPEMBlock(pemBlock) {
 243  		if password == nil {
 244  			return nil, errors.New("private key password is required for encrypted private keys")
 245  		}
 246  
 247  		if decrypted, e = x509.DecryptPEMBlock(pemBlock, password); e != nil {
 248  			return
 249  		}
 250  	}
 251  	key, e = parsePKCSPrivateKey(decrypted)
 252  	return
 253  }
 254  
 255  // ParsePrivateKey using PKCS1 or PKCS8
 256  func parsePKCSPrivateKey(decryptedKey []byte) (*rsa.PrivateKey, error) {
 257  	if key, err := x509.ParsePKCS1PrivateKey(decryptedKey); err == nil {
 258  		return key, nil
 259  	}
 260  	if key, err := x509.ParsePKCS8PrivateKey(decryptedKey); err == nil {
 261  		switch key := key.(type) {
 262  		case *rsa.PrivateKey:
 263  			return key, nil
 264  		default:
 265  			return nil, fmt.Errorf("unsupportesd private key type in PKCS8 wrapping")
 266  		}
 267  	}
 268  	return nil, fmt.Errorf("failed to parse private key")
 269  }
 270  
 271  // parseContentLength trims whitespace from cl and returns -1 if can't purse uint, or the value if it's no less than 0
 272  func parseContentLength(cl string) int64 {
 273  	cl = textproto.TrimString(cl)
 274  	n, err := strconv.ParseUint(cl, 10, 63)
 275  	if err != nil {
 276  		return -1
 277  	}
 278  	return int64(n)
 279  }
 280  
 281  func generateRandUUID() (string, error) {
 282  	b := make([]byte, 16)
 283  	_, err := rand.Read(b)
 284  	if err != nil {
 285  		return "", err
 286  	}
 287  	uuid := fmt.Sprintf("%x%x%x%x%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
 288  
 289  	return uuid, nil
 290  }
 291  
 292  func makeACopy(original []string) []string {
 293  	tmp := make([]string, len(original))
 294  	copy(tmp, original)
 295  	return tmp
 296  }
 297  
 298  // IsEnvVarFalse is used for checking if an environment variable is explicitly set to false, otherwise would set it true by default
 299  func IsEnvVarFalse(envVarKey string) bool {
 300  	val, existed := os.LookupEnv(envVarKey)
 301  	return existed && strings.ToLower(val) == "false"
 302  }
 303  
 304  // IsEnvVarTrue is used for checking if an environment variable is explicitly set to true, otherwise would set it true by default
 305  func IsEnvVarTrue(envVarKey string) bool {
 306  	val, existed := os.LookupEnv(envVarKey)
 307  	return existed && strings.ToLower(val) == "true"
 308  }
 309