mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-07 15:11:33 +02:00
fix: hmacauth dependency licensing issue (#3253)
* fix: upstream licensing issue by adopting hmacauth library and changing asserting library for its test cases Signed-off-by: Jan Larwig <jan@larwig.com> * fix: golang code quality and linting issues for hmacauth Signed-off-by: Jan Larwig <jan@larwig.com> --------- Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
package hmacauth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// These correspond to the headers used in bitly/oauth2_proxy#147.
|
||||
var HEADERS = []string{
|
||||
"Content-Length",
|
||||
"Content-Md5",
|
||||
"Content-Type",
|
||||
"Date",
|
||||
"Authorization",
|
||||
"X-Forwarded-User",
|
||||
"X-Forwarded-Email",
|
||||
"X-Forwarded-Access-Token",
|
||||
"Cookie",
|
||||
"Gap-Auth",
|
||||
}
|
||||
|
||||
func TestSupportedHashAlgorithm(t *testing.T) {
|
||||
algorithm, err := DigestNameToCryptoHash("sha1")
|
||||
assert.Equal(t, err, nil)
|
||||
assert.Equal(t, algorithm, crypto.SHA1)
|
||||
assert.Equal(t, algorithm.Available(), true)
|
||||
}
|
||||
|
||||
func TestUnsupportedHashAlgorithm(t *testing.T) {
|
||||
algorithm, err := DigestNameToCryptoHash("unsupported")
|
||||
assert.NotEqual(t, err, nil)
|
||||
assert.Equal(t, err.Error(),
|
||||
"hmacauth: hash algorithm not supported: unsupported")
|
||||
assert.Equal(t, algorithm, crypto.Hash(0))
|
||||
assert.Equal(t, algorithm.Available(), false)
|
||||
}
|
||||
|
||||
func TestResultUnsupportedAlgorithmWillCauseNewHmacAuthToPanic(t *testing.T) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
assert.Equal(t, err,
|
||||
"hmacauth: hash algorithm #0 is unavailable")
|
||||
}()
|
||||
NewHmacAuth(crypto.Hash(0), nil, "", nil)
|
||||
}
|
||||
|
||||
func newTestRequest(request ...string) (req *http.Request) {
|
||||
reqBuf := bufio.NewReader(
|
||||
strings.NewReader(strings.Join(request, "\n")))
|
||||
if req, err := http.ReadRequest(reqBuf); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return req
|
||||
}
|
||||
}
|
||||
|
||||
func testHmacAuth() HmacAuth {
|
||||
return NewHmacAuth(
|
||||
crypto.SHA1, []byte("foobar"), "GAP-Signature", HEADERS)
|
||||
}
|
||||
|
||||
func TestRequestSignaturePost(t *testing.T) {
|
||||
body := `{ "hello": "world!" }`
|
||||
req := newTestRequest(
|
||||
"POST /foo/bar HTTP/1.1",
|
||||
"Content-Length: "+strconv.Itoa(len(body)),
|
||||
"Content-MD5: deadbeef",
|
||||
"Content-Type: application/json",
|
||||
"Date: 2015-09-28",
|
||||
"Authorization: trust me",
|
||||
"X-Forwarded-User: mbland",
|
||||
"X-Forwarded-Email: mbland@acm.org",
|
||||
"X-Forwarded-Access-Token: feedbead",
|
||||
"Cookie: foo; bar; baz=quux",
|
||||
"Gap-Auth: mbland",
|
||||
"",
|
||||
body,
|
||||
)
|
||||
|
||||
h := testHmacAuth()
|
||||
assert.Equal(t, h.StringToSign(req), strings.Join([]string{
|
||||
"POST",
|
||||
strconv.Itoa(len(body)),
|
||||
"deadbeef",
|
||||
"application/json",
|
||||
"2015-09-28",
|
||||
"trust me",
|
||||
"mbland",
|
||||
"mbland@acm.org",
|
||||
"feedbead",
|
||||
"foo; bar; baz=quux",
|
||||
"mbland",
|
||||
"/foo/bar",
|
||||
}, "\n")+"\n")
|
||||
assert.Equal(t, h.RequestSignature(req),
|
||||
"sha1 K4IrVDtMCRwwW8Oms0VyZWMjXHI=")
|
||||
|
||||
if requestBody, err := io.ReadAll(req.Body); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
assert.Equal(t, string(requestBody), body)
|
||||
}
|
||||
}
|
||||
|
||||
func newGetRequest() *http.Request {
|
||||
return newTestRequest(
|
||||
"GET /foo/bar HTTP/1.1",
|
||||
"Date: 2015-09-29",
|
||||
"Cookie: foo; bar; baz=quux",
|
||||
"Gap-Auth: mbland",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
func TestRequestSignatureGetWithFullUrl(t *testing.T) {
|
||||
req := newTestRequest(
|
||||
"GET http://localhost/foo/bar?baz=quux%2Fxyzzy#plugh HTTP/1.1",
|
||||
"Date: 2015-09-29",
|
||||
"Cookie: foo; bar; baz=quux",
|
||||
"Gap-Auth: mbland",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
h := testHmacAuth()
|
||||
assert.Equal(t, h.StringToSign(req), strings.Join([]string{
|
||||
"GET",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"2015-09-29",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"foo; bar; baz=quux",
|
||||
"mbland",
|
||||
"/foo/bar?baz=quux%2Fxyzzy#plugh",
|
||||
}, "\n")+"\n")
|
||||
assert.Equal(t, h.RequestSignature(req),
|
||||
"sha1 ih5Jce9nsltry63rR4ImNz2hdnk=")
|
||||
}
|
||||
|
||||
func TestRequestSignatureGetWithMultipleHeadersWithTheSameName(t *testing.T) {
|
||||
// Just using "Cookie:" out of convenience.
|
||||
req := newTestRequest(
|
||||
"GET /foo/bar HTTP/1.1",
|
||||
"Date: 2015-09-29",
|
||||
"Cookie: foo",
|
||||
"Cookie: bar",
|
||||
"Cookie: baz=quux",
|
||||
"Gap-Auth: mbland",
|
||||
"",
|
||||
"",
|
||||
)
|
||||
|
||||
h := testHmacAuth()
|
||||
assert.Equal(t, h.StringToSign(req), strings.Join([]string{
|
||||
"GET",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"2015-09-29",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"foo,bar,baz=quux",
|
||||
"mbland",
|
||||
"/foo/bar",
|
||||
}, "\n")+"\n")
|
||||
assert.Equal(t, h.RequestSignature(req),
|
||||
"sha1 JlRkes1X+qq3Bgc/GcRyLos+4aI=")
|
||||
}
|
||||
|
||||
func TestAuthenticateRequestResultNoSignature(t *testing.T) {
|
||||
h := testHmacAuth()
|
||||
req := newGetRequest()
|
||||
result, header, computed := h.AuthenticateRequest(req)
|
||||
assert.Equal(t, result, ResultNoSignature)
|
||||
assert.Equal(t, header, "")
|
||||
assert.Equal(t, computed, "")
|
||||
}
|
||||
|
||||
func TestAuthenticateRequestResultInvalidFormat(t *testing.T) {
|
||||
h := testHmacAuth()
|
||||
req := newGetRequest()
|
||||
badValue := "should be algorithm and digest value"
|
||||
req.Header.Set("GAP-Signature", badValue)
|
||||
result, header, computed := h.AuthenticateRequest(req)
|
||||
assert.Equal(t, result, ResultInvalidFormat)
|
||||
assert.Equal(t, header, badValue)
|
||||
assert.Equal(t, computed, "")
|
||||
}
|
||||
|
||||
func TestAuthenticateRequestResultUnsupportedAlgorithm(t *testing.T) {
|
||||
h := testHmacAuth()
|
||||
req := newGetRequest()
|
||||
validSignature := h.RequestSignature(req)
|
||||
components := strings.Split(validSignature, " ")
|
||||
signatureWithResultUnsupportedAlgorithm := "unsupported " +
|
||||
components[1]
|
||||
req.Header.Set("GAP-Signature", signatureWithResultUnsupportedAlgorithm)
|
||||
result, header, computed := h.AuthenticateRequest(req)
|
||||
assert.Equal(t, result, ResultUnsupportedAlgorithm)
|
||||
assert.Equal(t, header, signatureWithResultUnsupportedAlgorithm)
|
||||
assert.Equal(t, computed, "")
|
||||
}
|
||||
|
||||
func TestAuthenticateRequestResultMatch(t *testing.T) {
|
||||
h := testHmacAuth()
|
||||
req := newGetRequest()
|
||||
expected := h.RequestSignature(req)
|
||||
h.SignRequest(req)
|
||||
result, header, computed := h.AuthenticateRequest(req)
|
||||
assert.Equal(t, result, ResultMatch)
|
||||
assert.Equal(t, header, expected)
|
||||
assert.Equal(t, computed, expected)
|
||||
}
|
||||
|
||||
func TestAuthenticateRequestMismatch(t *testing.T) {
|
||||
foobarAuth := testHmacAuth()
|
||||
barbazAuth := NewHmacAuth(
|
||||
crypto.SHA1, []byte("barbaz"), "GAP-Signature", HEADERS)
|
||||
req := newGetRequest()
|
||||
foobarAuth.SignRequest(req)
|
||||
result, header, computed := barbazAuth.AuthenticateRequest(req)
|
||||
assert.Equal(t, result, ResultMismatch)
|
||||
assert.Equal(t, header, foobarAuth.RequestSignature(req))
|
||||
assert.Equal(t, computed, barbazAuth.RequestSignature(req))
|
||||
}
|
||||
|
||||
type SignatureAuthenticator struct {
|
||||
auth HmacAuth
|
||||
}
|
||||
|
||||
func (v *SignatureAuthenticator) Authenticate(
|
||||
w http.ResponseWriter, r *http.Request) {
|
||||
result, headerSig, computedSig := v.auth.AuthenticateRequest(r)
|
||||
switch result {
|
||||
case ResultNoSignature:
|
||||
w.Write([]byte("no signature received"))
|
||||
case ResultMatch:
|
||||
w.Write([]byte("signatures match"))
|
||||
case ResultMismatch:
|
||||
w.Write([]byte("signatures do not match:" +
|
||||
"\n received: " + headerSig +
|
||||
"\n computed: " + computedSig))
|
||||
default:
|
||||
panic("Unknown result value: " + result.String())
|
||||
}
|
||||
}
|
||||
|
||||
// fakeNetConn simulates an http.Request.Body buffer that will be consumed
|
||||
// when it is read by the hmacauth.HmacAuth if not handled properly. See:
|
||||
//
|
||||
// https://github.com/18F/hmacauth/pull/4
|
||||
type fakeNetConn struct {
|
||||
reqBody string
|
||||
}
|
||||
|
||||
func (fnc *fakeNetConn) Read(p []byte) (n int, err error) {
|
||||
if bodyLen := len(fnc.reqBody); bodyLen != 0 {
|
||||
copy(p, fnc.reqBody)
|
||||
fnc.reqBody = ""
|
||||
return bodyLen, io.EOF
|
||||
}
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
func TestSendAuthenticatedPostRequestToServer(t *testing.T) {
|
||||
key := "foobar"
|
||||
payload := `{ "hello": "world!" }`
|
||||
|
||||
auth := NewHmacAuth(crypto.SHA1, []byte(key), "X-Test-Signature", nil)
|
||||
authenticator := &SignatureAuthenticator{auth: auth}
|
||||
upstream := httptest.NewServer(
|
||||
http.HandlerFunc(authenticator.Authenticate))
|
||||
|
||||
req, err := http.NewRequest("POST", upstream.URL+"/foo/bar",
|
||||
io.NopCloser(&fakeNetConn{reqBody: payload}))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
auth.SignRequest(req)
|
||||
if response, err := http.DefaultClient.Do(req); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
assert.Equal(t, response.StatusCode, http.StatusOK)
|
||||
responseBody, _ := io.ReadAll(response.Body)
|
||||
assert.Equal(t, "signatures match", string(responseBody))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user