github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/oracle/oci/client/transport.go (about)

     1  package oci
     2  
     3  import (
     4  	"bytes"
     5  	"crypto"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/sha256"
     9  	"encoding/base64"
    10  	"fmt"
    11  	"io"
    12  	"net/http"
    13  	"strconv"
    14  	"strings"
    15  	"time"
    16  )
    17  
    18  type nopCloser struct {
    19  	io.Reader
    20  }
    21  
    22  func (nopCloser) Close() error {
    23  	return nil
    24  }
    25  
    26  // Transport adds OCI signature authentication to each outgoing request.
    27  type Transport struct {
    28  	transport http.RoundTripper
    29  	config    *Config
    30  }
    31  
    32  // NewTransport creates a new Transport to add OCI signature authentication
    33  // to each outgoing request.
    34  func NewTransport(transport http.RoundTripper, config *Config) *Transport {
    35  	return &Transport{
    36  		transport: transport,
    37  		config:    config,
    38  	}
    39  }
    40  
    41  func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
    42  	var buf *bytes.Buffer
    43  
    44  	if req.Body != nil {
    45  		buf = new(bytes.Buffer)
    46  		buf.ReadFrom(req.Body)
    47  		req.Body = nopCloser{buf}
    48  	}
    49  	if req.Header.Get("date") == "" {
    50  		req.Header.Set("date", time.Now().UTC().Format(http.TimeFormat))
    51  	}
    52  	if req.Header.Get("content-type") == "" {
    53  		req.Header.Set("content-type", "application/json")
    54  	}
    55  	if req.Header.Get("accept") == "" {
    56  		req.Header.Set("accept", "application/json")
    57  	}
    58  	if req.Header.Get("host") == "" {
    59  		req.Header.Set("host", req.URL.Host)
    60  	}
    61  
    62  	var signheaders []string
    63  	if (req.Method == "PUT" || req.Method == "POST") && buf != nil {
    64  		signheaders = []string{"(request-target)", "host", "date",
    65  			"content-length", "content-type", "x-content-sha256"}
    66  
    67  		if req.Header.Get("content-length") == "" {
    68  			req.Header.Set("content-length", strconv.Itoa(buf.Len()))
    69  		}
    70  
    71  		hasher := sha256.New()
    72  		hasher.Write(buf.Bytes())
    73  		hash := hasher.Sum(nil)
    74  		req.Header.Set("x-content-sha256", base64.StdEncoding.EncodeToString(hash))
    75  	} else {
    76  		signheaders = []string{"date", "host", "(request-target)"}
    77  	}
    78  
    79  	var signbuffer bytes.Buffer
    80  	for idx, header := range signheaders {
    81  		signbuffer.WriteString(header)
    82  		signbuffer.WriteString(": ")
    83  
    84  		if header == "(request-target)" {
    85  			signbuffer.WriteString(strings.ToLower(req.Method))
    86  			signbuffer.WriteString(" ")
    87  			signbuffer.WriteString(req.URL.RequestURI())
    88  		} else {
    89  			signbuffer.WriteString(req.Header.Get(header))
    90  		}
    91  
    92  		if idx < len(signheaders)-1 {
    93  			signbuffer.WriteString("\n")
    94  		}
    95  	}
    96  
    97  	h := sha256.New()
    98  	h.Write(signbuffer.Bytes())
    99  	digest := h.Sum(nil)
   100  	signature, err := rsa.SignPKCS1v15(rand.Reader, t.config.Key, crypto.SHA256, digest)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	authHeader := fmt.Sprintf("Signature headers=\"%s\","+
   106  		"keyId=\"%s/%s/%s\","+
   107  		"algorithm=\"rsa-sha256\","+
   108  		"signature=\"%s\","+
   109  		"version=\"1\"",
   110  		strings.Join(signheaders, " "),
   111  		t.config.Tenancy, t.config.User, t.config.Fingerprint,
   112  		base64.StdEncoding.EncodeToString(signature))
   113  	req.Header.Add("Authorization", authHeader)
   114  
   115  	return t.transport.RoundTrip(req)
   116  }