Sign Upstream requests with HMAC. closes #147
This commit is contained in:
parent
7c241ec1fe
commit
e4626c1360
1
Godeps
1
Godeps
@ -1,3 +1,4 @@
|
||||
github.com/18F/hmacauth 1.0.1
|
||||
github.com/BurntSushi/toml 3883ac1ce943878302255f538fce319d23226223
|
||||
github.com/bitly/go-simplejson 3378bdcb5cebedcbf8b5750edee28010f128fe24
|
||||
github.com/mreiferson/go-options ee94b57f2fbf116075426f853e5abbcdfeca8b3d
|
||||
|
29
README.md
29
README.md
@ -113,15 +113,16 @@ An example [oauth2_proxy.cfg](contrib/oauth2_proxy.cfg.example) config file is i
|
||||
|
||||
```
|
||||
Usage of oauth2_proxy:
|
||||
-approval_prompt="force": Oauth approval_prompt
|
||||
-approval-prompt="force": Oauth approval_prompt
|
||||
-authenticated-emails-file="": authenticate against emails via file (one per line)
|
||||
-basic-auth-password="": the password to set when passing the HTTP Basic Auth header
|
||||
-client-id="": the OAuth Client ID: ie: "123456.apps.googleusercontent.com"
|
||||
-client-secret="": the OAuth Client Secret
|
||||
-config="": path to config file
|
||||
-cookie-domain="": an optional cookie domain to force cookies to (ie: .yourcompany.com)*
|
||||
-cookie-expire=168h0m0s: expire timeframe for cookie
|
||||
-cookie-httponly=true: set HttpOnly cookie flag
|
||||
-cookie-key="_oauth2_proxy": the name of the cookie that the oauth_proxy creates
|
||||
-cookie-name="_oauth2_proxy": the name of the cookie that the oauth_proxy creates
|
||||
-cookie-refresh=0: refresh the cookie after this duration; 0 to disable
|
||||
-cookie-secret="": the seed string for secure cookies
|
||||
-cookie-secure=true: set secure (HTTPS) cookie flag
|
||||
@ -130,17 +131,15 @@ Usage of oauth2_proxy:
|
||||
-email-domain=: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email
|
||||
-github-org="": restrict logins to members of this organisation
|
||||
-github-team="": restrict logins to members of this team
|
||||
-google-group="": restrict logins to members of this google group
|
||||
-google-admin-email="": the google admin to impersonate for api calls
|
||||
-google-group=: restrict logins to members of this google group (may be given multiple times).
|
||||
-google-service-account-json="": the path to the service account json credentials
|
||||
|
||||
-htpasswd-file="": additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption
|
||||
-http-address="127.0.0.1:4180": [http://]<addr>:<port> or unix://<path> to listen on for HTTP clients
|
||||
-https-address=":443": <addr>:<port> to listen on for HTTPS clients
|
||||
-login-url="": Authentication endpoint
|
||||
-pass-access-token=false: pass OAuth access_token to upstream via X-Forwarded-Access-Token header
|
||||
-pass-basic-auth=true: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream
|
||||
-basic-auth-password="": the password to set when passing the HTTP Basic Auth header
|
||||
-pass-host-header=true: pass the request Host Header to upstream
|
||||
-profile-url="": Profile access endpoint
|
||||
-provider="google": OAuth provider
|
||||
@ -149,6 +148,7 @@ Usage of oauth2_proxy:
|
||||
-redirect-url="": the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback"
|
||||
-request-logging=true: Log requests to stdout
|
||||
-scope="": Oauth scope specification
|
||||
-signature-key="": GAP-Signature request signature key (algorithm:secretkey)
|
||||
-skip-auth-regex=: bypass authentication for requests path's that match (may be given multiple times)
|
||||
-tls-cert="": path to certificate file
|
||||
-tls-key="": path to private key file
|
||||
@ -250,6 +250,24 @@ OAuth2 Proxy responds directly to the following endpoints. All other endpoints w
|
||||
* /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
|
||||
* /oauth2/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](#nginx-auth-request)
|
||||
|
||||
## Request signatures
|
||||
|
||||
If `signature_key` is defined, proxied requests will be signed with the
|
||||
`GAP-Signature` header, which is a [Hash-based Message Authentication Code
|
||||
(HMAC)](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code)
|
||||
of selected request information and the request body [see `SIGNATURE_HEADERS`
|
||||
in `oauthproxy.go`](./oauthproxy.go).
|
||||
|
||||
`signature_key` must be of the form `algorithm:secretkey`, (ie: `signature_key = "sha1:secret0"`)
|
||||
|
||||
For more information about HMAC request signature validation, read the
|
||||
following:
|
||||
|
||||
* [Amazon Web Services: Signing and Authenticating REST
|
||||
Requests](https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html)
|
||||
* [rc3.org: Using HMAC to authenticate Web service
|
||||
requests](http://rc3.org/2011/12/02/using-hmac-to-authenticate-web-service-requests/)
|
||||
|
||||
## Logging Format
|
||||
|
||||
OAuth2 Proxy logs requests to stdout in a format similar to Apache Combined Log.
|
||||
@ -258,7 +276,6 @@ OAuth2 Proxy logs requests to stdout in a format similar to Apache Combined Log.
|
||||
<REMOTE_ADDRESS> - <user@domain.com> [19/Mar/2015:17:20:19 -0400] <HOST_HEADER> GET <UPSTREAM_HOST> "/path/" HTTP/1.1 "<USER_AGENT>" <RESPONSE_CODE> <RESPONSE_BYTES> <REQUEST_DURATION>
|
||||
```
|
||||
|
||||
|
||||
## Adding a new Provider
|
||||
|
||||
Follow the examples in the [`providers` package](providers/) to define a new
|
||||
|
2
main.go
2
main.go
@ -69,6 +69,8 @@ func main() {
|
||||
flagSet.String("scope", "", "OAuth scope specification")
|
||||
flagSet.String("approval-prompt", "force", "OAuth approval_prompt")
|
||||
|
||||
flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
|
||||
|
||||
flagSet.Parse(os.Args[1:])
|
||||
|
||||
if *showVersion {
|
||||
|
@ -14,10 +14,26 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/18F/hmacauth"
|
||||
"github.com/bitly/oauth2_proxy/cookie"
|
||||
"github.com/bitly/oauth2_proxy/providers"
|
||||
)
|
||||
|
||||
const SignatureHeader = "GAP-Signature"
|
||||
|
||||
var SignatureHeaders []string = []string{
|
||||
"Content-Length",
|
||||
"Content-Md5",
|
||||
"Content-Type",
|
||||
"Date",
|
||||
"Authorization",
|
||||
"X-Forwarded-User",
|
||||
"X-Forwarded-Email",
|
||||
"X-Forwarded-Access-Token",
|
||||
"Cookie",
|
||||
"Gap-Auth",
|
||||
}
|
||||
|
||||
type OAuthProxy struct {
|
||||
CookieSeed string
|
||||
CookieName string
|
||||
@ -54,10 +70,15 @@ type OAuthProxy struct {
|
||||
type UpstreamProxy struct {
|
||||
upstream string
|
||||
handler http.Handler
|
||||
auth hmacauth.HmacAuth
|
||||
}
|
||||
|
||||
func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("GAP-Upstream-Address", u.upstream)
|
||||
if u.auth != nil {
|
||||
r.Header.Set("GAP-Auth", w.Header().Get("GAP-Auth"))
|
||||
u.auth.SignRequest(r)
|
||||
}
|
||||
u.handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
@ -89,6 +110,11 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) {
|
||||
|
||||
func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
serveMux := http.NewServeMux()
|
||||
var auth hmacauth.HmacAuth
|
||||
if sigData := opts.signatureData; sigData != nil {
|
||||
auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key),
|
||||
SignatureHeader, SignatureHeaders)
|
||||
}
|
||||
for _, u := range opts.proxyURLs {
|
||||
path := u.Path
|
||||
switch u.Scheme {
|
||||
@ -101,14 +127,15 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
} else {
|
||||
setProxyDirector(proxy)
|
||||
}
|
||||
serveMux.Handle(path, &UpstreamProxy{u.Host, proxy})
|
||||
serveMux.Handle(path,
|
||||
&UpstreamProxy{u.Host, proxy, auth})
|
||||
case "file":
|
||||
if u.Fragment != "" {
|
||||
path = u.Fragment
|
||||
}
|
||||
log.Printf("mapping path %q => file system %q", path, u.Path)
|
||||
proxy := NewFileServer(path, u.Path)
|
||||
serveMux.Handle(path, &UpstreamProxy{path, proxy})
|
||||
serveMux.Handle(path, &UpstreamProxy{path, proxy, nil})
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown upstream protocol %s", u.Scheme))
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/base64"
|
||||
"github.com/18F/hmacauth"
|
||||
"github.com/bitly/oauth2_proxy/providers"
|
||||
"github.com/bmizerany/assert"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
@ -88,6 +91,45 @@ func TestRobotsTxt(t *testing.T) {
|
||||
assert.Equal(t, "User-agent: *\nDisallow: /", rw.Body.String())
|
||||
}
|
||||
|
||||
type TestProvider struct {
|
||||
*providers.ProviderData
|
||||
EmailAddress string
|
||||
ValidToken bool
|
||||
}
|
||||
|
||||
func NewTestProvider(provider_url *url.URL, email_address string) *TestProvider {
|
||||
return &TestProvider{
|
||||
ProviderData: &providers.ProviderData{
|
||||
ProviderName: "Test Provider",
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/authorize",
|
||||
},
|
||||
RedeemURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/token",
|
||||
},
|
||||
ProfileURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/api/v1/profile",
|
||||
},
|
||||
Scope: "profile.email",
|
||||
},
|
||||
EmailAddress: email_address,
|
||||
}
|
||||
}
|
||||
|
||||
func (tp *TestProvider) GetEmailAddress(session *providers.SessionState) (string, error) {
|
||||
return tp.EmailAddress, nil
|
||||
}
|
||||
|
||||
func (tp *TestProvider) ValidateSessionState(session *providers.SessionState) bool {
|
||||
return tp.ValidToken
|
||||
}
|
||||
|
||||
func TestBasicAuthPassword(t *testing.T) {
|
||||
provider_server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
log.Printf("%#v", r)
|
||||
@ -121,29 +163,7 @@ func TestBasicAuthPassword(t *testing.T) {
|
||||
const email_address = "michael.bland@gsa.gov"
|
||||
const user_name = "michael.bland"
|
||||
|
||||
opts.provider = &TestProvider{
|
||||
ProviderData: &providers.ProviderData{
|
||||
ProviderName: "Test Provider",
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/authorize",
|
||||
},
|
||||
RedeemURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/token",
|
||||
},
|
||||
ProfileURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/api/v1/profile",
|
||||
},
|
||||
Scope: "profile.email",
|
||||
},
|
||||
EmailAddress: email_address,
|
||||
}
|
||||
|
||||
opts.provider = NewTestProvider(provider_url, email_address)
|
||||
proxy := NewOAuthProxy(opts, func(email string) bool {
|
||||
return email == email_address
|
||||
})
|
||||
@ -183,20 +203,6 @@ func TestBasicAuthPassword(t *testing.T) {
|
||||
provider_server.Close()
|
||||
}
|
||||
|
||||
type TestProvider struct {
|
||||
*providers.ProviderData
|
||||
EmailAddress string
|
||||
ValidToken bool
|
||||
}
|
||||
|
||||
func (tp *TestProvider) GetEmailAddress(session *providers.SessionState) (string, error) {
|
||||
return tp.EmailAddress, nil
|
||||
}
|
||||
|
||||
func (tp *TestProvider) ValidateSessionState(session *providers.SessionState) bool {
|
||||
return tp.ValidToken
|
||||
}
|
||||
|
||||
type PassAccessTokenTest struct {
|
||||
provider_server *httptest.Server
|
||||
proxy *OAuthProxy
|
||||
@ -242,29 +248,7 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
|
||||
provider_url, _ := url.Parse(t.provider_server.URL)
|
||||
const email_address = "michael.bland@gsa.gov"
|
||||
|
||||
t.opts.provider = &TestProvider{
|
||||
ProviderData: &providers.ProviderData{
|
||||
ProviderName: "Test Provider",
|
||||
LoginURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/authorize",
|
||||
},
|
||||
RedeemURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/oauth/token",
|
||||
},
|
||||
ProfileURL: &url.URL{
|
||||
Scheme: "http",
|
||||
Host: provider_url.Host,
|
||||
Path: "/api/v1/profile",
|
||||
},
|
||||
Scope: "profile.email",
|
||||
},
|
||||
EmailAddress: email_address,
|
||||
}
|
||||
|
||||
t.opts.provider = NewTestProvider(provider_url, email_address)
|
||||
t.proxy = NewOAuthProxy(t.opts, func(email string) bool {
|
||||
return email == email_address
|
||||
})
|
||||
@ -559,7 +543,7 @@ func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
|
||||
func NewAuthOnlyEndpointTest() *ProcessCookieTest {
|
||||
pc_test := NewProcessCookieTestWithDefaults()
|
||||
pc_test.req, _ = http.NewRequest("GET",
|
||||
pc_test.opts.ProxyPrefix + "/auth", nil)
|
||||
pc_test.opts.ProxyPrefix+"/auth", nil)
|
||||
return pc_test
|
||||
}
|
||||
|
||||
@ -610,3 +594,143 @@ func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
|
||||
bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
|
||||
assert.Equal(t, "unauthorized request\n", string(bodyBytes))
|
||||
}
|
||||
|
||||
type SignatureAuthenticator struct {
|
||||
auth hmacauth.HmacAuth
|
||||
}
|
||||
|
||||
func (v *SignatureAuthenticator) Authenticate(
|
||||
w http.ResponseWriter, r *http.Request) {
|
||||
result, headerSig, computedSig := v.auth.AuthenticateRequest(r)
|
||||
if result == hmacauth.ResultNoSignature {
|
||||
w.Write([]byte("no signature received"))
|
||||
} else if result == hmacauth.ResultMatch {
|
||||
w.Write([]byte("signatures match"))
|
||||
} else if result == hmacauth.ResultMismatch {
|
||||
w.Write([]byte("signatures do not match:" +
|
||||
"\n received: " + headerSig +
|
||||
"\n computed: " + computedSig))
|
||||
} else {
|
||||
panic("Unknown result value: " + result.String())
|
||||
}
|
||||
}
|
||||
|
||||
type SignatureTest struct {
|
||||
opts *Options
|
||||
upstream *httptest.Server
|
||||
upstream_host string
|
||||
provider *httptest.Server
|
||||
header http.Header
|
||||
rw *httptest.ResponseRecorder
|
||||
authenticator *SignatureAuthenticator
|
||||
}
|
||||
|
||||
func NewSignatureTest() *SignatureTest {
|
||||
opts := NewOptions()
|
||||
opts.CookieSecret = "cookie secret"
|
||||
opts.ClientID = "client ID"
|
||||
opts.ClientSecret = "client secret"
|
||||
opts.EmailDomains = []string{"acm.org"}
|
||||
|
||||
authenticator := &SignatureAuthenticator{}
|
||||
upstream := httptest.NewServer(
|
||||
http.HandlerFunc(authenticator.Authenticate))
|
||||
upstream_url, _ := url.Parse(upstream.URL)
|
||||
opts.Upstreams = append(opts.Upstreams, upstream.URL)
|
||||
|
||||
providerHandler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{"access_token": "my_auth_token"}`))
|
||||
}
|
||||
provider := httptest.NewServer(http.HandlerFunc(providerHandler))
|
||||
provider_url, _ := url.Parse(provider.URL)
|
||||
opts.provider = NewTestProvider(provider_url, "mbland@acm.org")
|
||||
|
||||
return &SignatureTest{
|
||||
opts,
|
||||
upstream,
|
||||
upstream_url.Host,
|
||||
provider,
|
||||
make(http.Header),
|
||||
httptest.NewRecorder(),
|
||||
authenticator,
|
||||
}
|
||||
}
|
||||
|
||||
func (st *SignatureTest) Close() {
|
||||
st.provider.Close()
|
||||
st.upstream.Close()
|
||||
}
|
||||
|
||||
// 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 (st *SignatureTest) MakeRequestWithExpectedKey(method, body, key string) {
|
||||
err := st.opts.Validate()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
proxy := NewOAuthProxy(st.opts, func(email string) bool { return true })
|
||||
|
||||
var bodyBuf io.ReadCloser
|
||||
if body != "" {
|
||||
bodyBuf = ioutil.NopCloser(&fakeNetConn{reqBody: body})
|
||||
}
|
||||
req, err := http.NewRequest(method, "/foo/bar", bodyBuf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
req.Header = st.header
|
||||
|
||||
state := &providers.SessionState{
|
||||
Email: "mbland@acm.org", AccessToken: "my_access_token"}
|
||||
value, err := proxy.provider.CookieForSession(state, proxy.CookieCipher)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cookie := proxy.MakeCookie(req, value, proxy.CookieExpire, time.Now())
|
||||
req.AddCookie(cookie)
|
||||
// This is used by the upstream to validate the signature.
|
||||
st.authenticator.auth = hmacauth.NewHmacAuth(
|
||||
crypto.SHA1, []byte(key), SignatureHeader, SignatureHeaders)
|
||||
proxy.ServeHTTP(st.rw, req)
|
||||
}
|
||||
|
||||
func TestNoRequestSignature(t *testing.T) {
|
||||
st := NewSignatureTest()
|
||||
defer st.Close()
|
||||
st.MakeRequestWithExpectedKey("GET", "", "")
|
||||
assert.Equal(t, 200, st.rw.Code)
|
||||
assert.Equal(t, st.rw.Body.String(), "no signature received")
|
||||
}
|
||||
|
||||
func TestRequestSignatureGetRequest(t *testing.T) {
|
||||
st := NewSignatureTest()
|
||||
defer st.Close()
|
||||
st.opts.SignatureKey = "sha1:foobar"
|
||||
st.MakeRequestWithExpectedKey("GET", "", "foobar")
|
||||
assert.Equal(t, 200, st.rw.Code)
|
||||
assert.Equal(t, st.rw.Body.String(), "signatures match")
|
||||
}
|
||||
|
||||
func TestRequestSignaturePostRequest(t *testing.T) {
|
||||
st := NewSignatureTest()
|
||||
defer st.Close()
|
||||
st.opts.SignatureKey = "sha1:foobar"
|
||||
payload := `{ "hello": "world!" }`
|
||||
st.MakeRequestWithExpectedKey("POST", payload, "foobar")
|
||||
assert.Equal(t, 200, st.rw.Code)
|
||||
assert.Equal(t, st.rw.Body.String(), "signatures match")
|
||||
}
|
||||
|
33
options.go
33
options.go
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -8,6 +9,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/18F/hmacauth"
|
||||
"github.com/bitly/oauth2_proxy/providers"
|
||||
)
|
||||
|
||||
@ -60,11 +62,19 @@ type Options struct {
|
||||
|
||||
RequestLogging bool `flag:"request-logging" cfg:"request_logging"`
|
||||
|
||||
SignatureKey string `flag:"signature-key" cfg:"signature_key"`
|
||||
|
||||
// internal values that are set after config validation
|
||||
redirectURL *url.URL
|
||||
proxyURLs []*url.URL
|
||||
CompiledRegex []*regexp.Regexp
|
||||
provider providers.Provider
|
||||
signatureData *SignatureData
|
||||
}
|
||||
|
||||
type SignatureData struct {
|
||||
hash crypto.Hash
|
||||
key string
|
||||
}
|
||||
|
||||
func NewOptions() *Options {
|
||||
@ -175,6 +185,8 @@ func (o *Options) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
msgs = parseSignatureKey(o, msgs)
|
||||
|
||||
if len(msgs) != 0 {
|
||||
return fmt.Errorf("Invalid configuration:\n %s",
|
||||
strings.Join(msgs, "\n "))
|
||||
@ -210,3 +222,24 @@ func parseProviderInfo(o *Options, msgs []string) []string {
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
func parseSignatureKey(o *Options, msgs []string) []string {
|
||||
if o.SignatureKey == "" {
|
||||
return msgs
|
||||
}
|
||||
|
||||
components := strings.Split(o.SignatureKey, ":")
|
||||
if len(components) != 2 {
|
||||
return append(msgs, "invalid signature hash:key spec: "+
|
||||
o.SignatureKey)
|
||||
}
|
||||
|
||||
algorithm, secretKey := components[0], components[1]
|
||||
if hash, err := hmacauth.DigestNameToCryptoHash(algorithm); err != nil {
|
||||
return append(msgs, "unsupported signature hash algorithm: "+
|
||||
o.SignatureKey)
|
||||
} else {
|
||||
o.signatureData = &SignatureData{hash, secretKey}
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -166,3 +167,27 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
|
||||
o.CookieRefresh -= time.Duration(1)
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
func TestValidateSignatureKey(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "sha1:secret"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
assert.Equal(t, o.signatureData.hash, crypto.SHA1)
|
||||
assert.Equal(t, o.signatureData.key, "secret")
|
||||
}
|
||||
|
||||
func TestValidateSignatureKeyInvalidSpec(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "invalid spec"
|
||||
err := o.Validate()
|
||||
assert.Equal(t, err.Error(), "Invalid configuration:\n"+
|
||||
" invalid signature hash:key spec: "+o.SignatureKey)
|
||||
}
|
||||
|
||||
func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "unsupported:default secret"
|
||||
err := o.Validate()
|
||||
assert.Equal(t, err.Error(), "Invalid configuration:\n"+
|
||||
" unsupported signature hash algorithm: "+o.SignatureKey)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user