github.com/hugorut/terraform@v1.1.3/src/lang/funcs/encoding.go (about)

     1  package funcs
     2  
     3  import (
     4  	"bytes"
     5  	"compress/gzip"
     6  	"encoding/base64"
     7  	"fmt"
     8  	"log"
     9  	"net/url"
    10  	"unicode/utf8"
    11  
    12  	"github.com/zclconf/go-cty/cty"
    13  	"github.com/zclconf/go-cty/cty/function"
    14  	"golang.org/x/text/encoding/ianaindex"
    15  )
    16  
    17  // Base64DecodeFunc constructs a function that decodes a string containing a base64 sequence.
    18  var Base64DecodeFunc = function.New(&function.Spec{
    19  	Params: []function.Parameter{
    20  		{
    21  			Name: "str",
    22  			Type: cty.String,
    23  		},
    24  	},
    25  	Type: function.StaticReturnType(cty.String),
    26  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
    27  		s := args[0].AsString()
    28  		sDec, err := base64.StdEncoding.DecodeString(s)
    29  		if err != nil {
    30  			return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode base64 data '%s'", s)
    31  		}
    32  		if !utf8.Valid([]byte(sDec)) {
    33  			log.Printf("[DEBUG] the result of decoding the provided string is not valid UTF-8: %s", sDec)
    34  			return cty.UnknownVal(cty.String), fmt.Errorf("the result of decoding the provided string is not valid UTF-8")
    35  		}
    36  		return cty.StringVal(string(sDec)), nil
    37  	},
    38  })
    39  
    40  // Base64EncodeFunc constructs a function that encodes a string to a base64 sequence.
    41  var Base64EncodeFunc = function.New(&function.Spec{
    42  	Params: []function.Parameter{
    43  		{
    44  			Name: "str",
    45  			Type: cty.String,
    46  		},
    47  	},
    48  	Type: function.StaticReturnType(cty.String),
    49  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
    50  		return cty.StringVal(base64.StdEncoding.EncodeToString([]byte(args[0].AsString()))), nil
    51  	},
    52  })
    53  
    54  // TextEncodeBase64Func constructs a function that encodes a string to a target encoding and then to a base64 sequence.
    55  var TextEncodeBase64Func = function.New(&function.Spec{
    56  	Params: []function.Parameter{
    57  		{
    58  			Name: "string",
    59  			Type: cty.String,
    60  		},
    61  		{
    62  			Name: "encoding",
    63  			Type: cty.String,
    64  		},
    65  	},
    66  	Type: function.StaticReturnType(cty.String),
    67  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
    68  		encoding, err := ianaindex.IANA.Encoding(args[1].AsString())
    69  		if err != nil || encoding == nil {
    70  			return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString())
    71  		}
    72  
    73  		encName, err := ianaindex.IANA.Name(encoding)
    74  		if err != nil { // would be weird, since we just read this encoding out
    75  			encName = args[1].AsString()
    76  		}
    77  
    78  		encoder := encoding.NewEncoder()
    79  		encodedInput, err := encoder.Bytes([]byte(args[0].AsString()))
    80  		if err != nil {
    81  			// The string representations of "err" disclose implementation
    82  			// details of the underlying library, and the main error we might
    83  			// like to return a special message for is unexported as
    84  			// golang.org/x/text/encoding/src.RepertoireError, so this
    85  			// is just a generic error message for now.
    86  			//
    87  			// We also don't include the string itself in the message because
    88  			// it can typically be very large, contain newline characters,
    89  			// etc.
    90  			return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains characters that cannot be represented in %s", encName)
    91  		}
    92  
    93  		return cty.StringVal(base64.StdEncoding.EncodeToString(encodedInput)), nil
    94  	},
    95  })
    96  
    97  // TextDecodeBase64Func constructs a function that decodes a base64 sequence to a target encoding.
    98  var TextDecodeBase64Func = function.New(&function.Spec{
    99  	Params: []function.Parameter{
   100  		{
   101  			Name: "source",
   102  			Type: cty.String,
   103  		},
   104  		{
   105  			Name: "encoding",
   106  			Type: cty.String,
   107  		},
   108  	},
   109  	Type: function.StaticReturnType(cty.String),
   110  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
   111  		encoding, err := ianaindex.IANA.Encoding(args[1].AsString())
   112  		if err != nil || encoding == nil {
   113  			return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "%q is not a supported IANA encoding name or alias in this Terraform version", args[1].AsString())
   114  		}
   115  
   116  		encName, err := ianaindex.IANA.Name(encoding)
   117  		if err != nil { // would be weird, since we just read this encoding out
   118  			encName = args[1].AsString()
   119  		}
   120  
   121  		s := args[0].AsString()
   122  		sDec, err := base64.StdEncoding.DecodeString(s)
   123  		if err != nil {
   124  			switch err := err.(type) {
   125  			case base64.CorruptInputError:
   126  				return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given value is has an invalid base64 symbol at offset %d", int(err))
   127  			default:
   128  				return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "invalid source string: %T", err)
   129  			}
   130  
   131  		}
   132  
   133  		decoder := encoding.NewDecoder()
   134  		decoded, err := decoder.Bytes(sDec)
   135  		if err != nil || bytes.ContainsRune(decoded, '�') {
   136  			return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "the given string contains symbols that are not defined for %s", encName)
   137  		}
   138  
   139  		return cty.StringVal(string(decoded)), nil
   140  	},
   141  })
   142  
   143  // Base64GzipFunc constructs a function that compresses a string with gzip and then encodes the result in
   144  // Base64 encoding.
   145  var Base64GzipFunc = function.New(&function.Spec{
   146  	Params: []function.Parameter{
   147  		{
   148  			Name: "str",
   149  			Type: cty.String,
   150  		},
   151  	},
   152  	Type: function.StaticReturnType(cty.String),
   153  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
   154  		s := args[0].AsString()
   155  
   156  		var b bytes.Buffer
   157  		gz := gzip.NewWriter(&b)
   158  		if _, err := gz.Write([]byte(s)); err != nil {
   159  			return cty.UnknownVal(cty.String), fmt.Errorf("failed to write gzip raw data: '%s'", s)
   160  		}
   161  		if err := gz.Flush(); err != nil {
   162  			return cty.UnknownVal(cty.String), fmt.Errorf("failed to flush gzip writer: '%s'", s)
   163  		}
   164  		if err := gz.Close(); err != nil {
   165  			return cty.UnknownVal(cty.String), fmt.Errorf("failed to close gzip writer: '%s'", s)
   166  		}
   167  		return cty.StringVal(base64.StdEncoding.EncodeToString(b.Bytes())), nil
   168  	},
   169  })
   170  
   171  // URLEncodeFunc constructs a function that applies URL encoding to a given string.
   172  var URLEncodeFunc = function.New(&function.Spec{
   173  	Params: []function.Parameter{
   174  		{
   175  			Name: "str",
   176  			Type: cty.String,
   177  		},
   178  	},
   179  	Type: function.StaticReturnType(cty.String),
   180  	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
   181  		return cty.StringVal(url.QueryEscape(args[0].AsString())), nil
   182  	},
   183  })
   184  
   185  // Base64Decode decodes a string containing a base64 sequence.
   186  //
   187  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   188  //
   189  // Strings in the Terraform language are sequences of unicode characters rather
   190  // than bytes, so this function will also interpret the resulting bytes as
   191  // UTF-8. If the bytes after Base64 decoding are _not_ valid UTF-8, this function
   192  // produces an error.
   193  func Base64Decode(str cty.Value) (cty.Value, error) {
   194  	return Base64DecodeFunc.Call([]cty.Value{str})
   195  }
   196  
   197  // Base64Encode applies Base64 encoding to a string.
   198  //
   199  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   200  //
   201  // Strings in the Terraform language are sequences of unicode characters rather
   202  // than bytes, so this function will first encode the characters from the string
   203  // as UTF-8, and then apply Base64 encoding to the result.
   204  func Base64Encode(str cty.Value) (cty.Value, error) {
   205  	return Base64EncodeFunc.Call([]cty.Value{str})
   206  }
   207  
   208  // Base64Gzip compresses a string with gzip and then encodes the result in
   209  // Base64 encoding.
   210  //
   211  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   212  //
   213  // Strings in the Terraform language are sequences of unicode characters rather
   214  // than bytes, so this function will first encode the characters from the string
   215  // as UTF-8, then apply gzip compression, and then finally apply Base64 encoding.
   216  func Base64Gzip(str cty.Value) (cty.Value, error) {
   217  	return Base64GzipFunc.Call([]cty.Value{str})
   218  }
   219  
   220  // URLEncode applies URL encoding to a given string.
   221  //
   222  // This function identifies characters in the given string that would have a
   223  // special meaning when included as a query string argument in a URL and
   224  // escapes them using RFC 3986 "percent encoding".
   225  //
   226  // If the given string contains non-ASCII characters, these are first encoded as
   227  // UTF-8 and then percent encoding is applied separately to each UTF-8 byte.
   228  func URLEncode(str cty.Value) (cty.Value, error) {
   229  	return URLEncodeFunc.Call([]cty.Value{str})
   230  }
   231  
   232  // TextEncodeBase64 applies Base64 encoding to a string that was encoded before with a target encoding.
   233  //
   234  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   235  //
   236  // First step is to apply the target IANA encoding (e.g. UTF-16LE).
   237  // Strings in the Terraform language are sequences of unicode characters rather
   238  // than bytes, so this function will first encode the characters from the string
   239  // as UTF-8, and then apply Base64 encoding to the result.
   240  func TextEncodeBase64(str, enc cty.Value) (cty.Value, error) {
   241  	return TextEncodeBase64Func.Call([]cty.Value{str, enc})
   242  }
   243  
   244  // TextDecodeBase64 decodes a string containing a base64 sequence whereas a specific encoding of the string is expected.
   245  //
   246  // Terraform uses the "standard" Base64 alphabet as defined in RFC 4648 section 4.
   247  //
   248  // Strings in the Terraform language are sequences of unicode characters rather
   249  // than bytes, so this function will also interpret the resulting bytes as
   250  // the target encoding.
   251  func TextDecodeBase64(str, enc cty.Value) (cty.Value, error) {
   252  	return TextDecodeBase64Func.Call([]cty.Value{str, enc})
   253  }