github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/api/common/sign_test.go (about)

     1  package common
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"io/ioutil"
     7  	"net"
     8  	"net/http"
     9  	"net/url"
    10  	"time"
    11  
    12  	. "gopkg.in/check.v1"
    13  )
    14  
    15  type SignTests struct {
    16  	req *http.Request
    17  }
    18  
    19  var _ = Suite(&SignTests{})
    20  
    21  func (t *SignTests) SetUpTest(c *C) {
    22  	req, err := http.NewRequest("GET", "http://example.org/repositories", nil)
    23  	c.Assert(err, IsNil)
    24  	SignWithPassphrase(req, adminSecret)
    25  	t.req = req
    26  }
    27  
    28  func (t *SignTests) adminSigned() bool {
    29  	return ValidateRequest(t.req, adminPubkey, time.Minute)
    30  }
    31  
    32  func (t *SignTests) TestAuthorizationHeader(c *C) {
    33  	c.Assert(t.req.Header.Get("Authorization"), Not(Equals), "")
    34  }
    35  
    36  func (t *SignTests) TestAdminSigningCorrectSignature(c *C) {
    37  	c.Assert(t.adminSigned(), Equals, true)
    38  }
    39  
    40  func (t *SignTests) TestAdminSigningIgnoreUserAgent(c *C) {
    41  	t.req.Header.Set("User-Agent", "foo")
    42  	c.Assert(t.adminSigned(), Equals, true)
    43  }
    44  
    45  // TestSigningAvoidHeaderMixup verifies that different parts
    46  // of the request may not be confused with other parts by the
    47  // signature algorithm.
    48  func (t *SignTests) TestSigningAvoidHeaderMixup(c *C) {
    49  	t.req.Header.Set("Header", "value")
    50  	SignWithPassphrase(t.req, adminSecret)
    51  	c.Assert(t.adminSigned(), Equals, true)
    52  	t.req.Header.Del("Header")
    53  	t.req.Header.Set("Headerval", "ue")
    54  	c.Assert(t.adminSigned(), Equals, false)
    55  }
    56  
    57  func (t *SignTests) TestAdminSigningIgnoreHost(c *C) {
    58  	// we ignore the Host header as it breaks signing due to
    59  	// differences in client-side and server-side requests;
    60  	// the actual host name is still signed as part of the URL
    61  	t.req.Header.Set("Host", "foo")
    62  	c.Assert(t.adminSigned(), Equals, true)
    63  }
    64  
    65  func (t *SignTests) TestAdminSigningIgnoreAcceptEncoding(c *C) {
    66  	t.req.Header.Set("Accept-Encoding", "foo")
    67  	c.Assert(t.adminSigned(), Equals, true)
    68  }
    69  
    70  func (t *SignTests) TestAdminSigningNormalizeURL(c *C) {
    71  	t.req.URL.Host = ""
    72  	t.req.Host = "example.org"
    73  	c.Assert(t.adminSigned(), Equals, true)
    74  }
    75  
    76  func (t *SignTests) TestAdminSigningEmptyAuthorizationHeader(c *C) {
    77  	t.req.Header.Set("Authorization", "")
    78  	c.Assert(t.adminSigned(), Equals, false)
    79  }
    80  
    81  func (t *SignTests) TestAdminSigningNonLaraAuthorizationHeader(c *C) {
    82  	t.req.Header.Set("Authorization", "basic foo")
    83  	c.Assert(t.adminSigned(), Equals, false)
    84  }
    85  
    86  func (t *SignTests) TestAdminSigningShortSig(c *C) {
    87  	t.req.Header.Set("Authorization", "lara 111")
    88  	c.Assert(t.adminSigned(), Equals, false)
    89  }
    90  
    91  func (t *SignTests) TestAdminSigningMissingAuthorizationSig(c *C) {
    92  	t.req.Header.Set("Authorization", "lara ")
    93  	c.Assert(t.adminSigned(), Equals, false)
    94  }
    95  
    96  func (t *SignTests) TestAdminSigningBadHexHash(c *C) {
    97  	t.req.Header.Set("Authorization", "lara 123")
    98  	c.Assert(t.adminSigned(), Equals, false)
    99  }
   100  
   101  func (t *SignTests) TestAdminSigningHashTooShort(c *C) {
   102  	t.req.Header.Set("Authorization", "lara 1234")
   103  	c.Assert(t.adminSigned(), Equals, false)
   104  }
   105  
   106  func (t *SignTests) TestAdminSigningChangedMethodAuthorizationHeader(c *C) {
   107  	t.req.Method = "POST"
   108  	c.Assert(t.adminSigned(), Equals, false)
   109  }
   110  
   111  func (t *SignTests) TestAdminSigningAddedHeader(c *C) {
   112  	t.req.Header.Set("Test", "1")
   113  	c.Assert(t.adminSigned(), Equals, false)
   114  }
   115  
   116  func (t *SignTests) TestAdminSigningChangedUrl(c *C) {
   117  	newURL, err := url.Parse("http://example.org/repositories?x=3")
   118  	c.Assert(err, IsNil)
   119  	t.req.URL = newURL
   120  	c.Assert(t.adminSigned(), Equals, false)
   121  }
   122  
   123  func (t *SignTests) TestAdminSigningOutdatedSignature(c *C) {
   124  	tenSecsAgo := time.Now().Add(-10 * time.Second)
   125  	// this will most likely send non-GMT time which is against the HTTP RFC;
   126  	// as we should handle this as well, it's ok for testing:
   127  	t.req.Header.Set("Date", tenSecsAgo.Format(time.RFC1123))
   128  	SignWithPassphrase(t.req, adminSecret)
   129  	c.Assert(ValidateRequest(t.req, adminPubkey, 9*time.Second), Equals, false)
   130  }
   131  
   132  func (t *SignTests) changeBody() {
   133  	t.req.Body = ioutil.NopCloser(bytes.NewBuffer([]byte("changed body")))
   134  }
   135  
   136  func (t *SignTests) TestAdminSigningBodyChange(c *C) {
   137  	t.changeBody()
   138  	c.Assert(t.adminSigned(), Equals, false)
   139  }
   140  
   141  func (t *SignTests) TestAdminSigningBodyTextRead(c *C) {
   142  	t.changeBody()
   143  	c.Assert(t.adminSigned(), Equals, false)
   144  	buf := make([]byte, 100)
   145  	read, _ := t.req.Body.Read(buf)
   146  	c.Assert(string(buf[:read]), Equals, "changed body")
   147  }
   148  
   149  func (t *SignTests) TestYoungerThanBadHeader(c *C) {
   150  	t.req.Header.Set("Date", "123")
   151  	c.Assert(youngerThan(t.req, time.Minute), Equals, false)
   152  }
   153  
   154  func (t *SignTests) TestRealRoundTripWithBody(c *C) {
   155  	t.runRealRoundTrip(c, bytes.NewBufferString("test"))
   156  }
   157  
   158  func (t *SignTests) TestRealRoundTripWithEmptyBody(c *C) {
   159  	t.runRealRoundTrip(c, nil)
   160  }
   161  
   162  // TestRealRoundTrip uses the full Go http client/server stack to execute
   163  // a real HTTP roundtrip.
   164  // This ensures that this process does not mangle the request in a way
   165  // which would break signatures.
   166  func (t *SignTests) runRealRoundTrip(c *C, body io.Reader) {
   167  	// passing port :0 to Listen lets it choose a random port
   168  	listener, err := net.Listen("tcp", "127.0.0.1:0")
   169  	c.Assert(err, IsNil)
   170  	defer listener.Close()
   171  	hostAndPort := listener.Addr().String()
   172  
   173  	adminSecret := []byte("test")
   174  	pubKey, err := GetAdminSecretPubkey(adminSecret)
   175  	c.Assert(err, IsNil)
   176  
   177  	server := &http.Server{
   178  		Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   179  			if !ValidateRequest(req, pubKey, 5*time.Second) {
   180  				rw.WriteHeader(http.StatusUnauthorized)
   181  				return
   182  			}
   183  			rw.Header().Set("X-Lara-Validated", "1")
   184  		}),
   185  	}
   186  	go server.Serve(listener)
   187  
   188  	req, err := http.NewRequest("GET", "http://"+hostAndPort+"/foo.txt?x=1",
   189  		body)
   190  	c.Assert(err, IsNil)
   191  	SignWithPassphrase(req, adminSecret)
   192  	client := &http.Client{}
   193  	resp, err := client.Do(req)
   194  	c.Assert(err, IsNil)
   195  	c.Assert(resp.StatusCode, Equals, 200)
   196  	c.Assert(resp.Header.Get("X-Lara-Validated"), Equals, "1")
   197  }