Browse Source

add Authorization to httpproxy

wenzuochao 6 years ago
parent
commit
ddc730b9ea

+ 6 - 2
integration/core_test.go

@@ -1,6 +1,8 @@
 package integration
 
 import (
+	"fmt"
+	"github.com/goji/httpauth"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httputil"
@@ -263,7 +265,8 @@ func handlerFake(w http.ResponseWriter, r *http.Request) {
 }
 
 func handlerFakeServer() (server *httptest.Server) {
-	server = httptest.NewServer(http.HandlerFunc(handlerFake))
+	handleFunc := httpauth.SimpleBasicAuth("someuser", "somepassword")(http.HandlerFunc(handlerFake))
+	server = httptest.NewServer(handleFunc)
 
 	return server
 }
@@ -295,7 +298,8 @@ func Test_HTTPProxy(t *testing.T) {
 	assert.Equal(t, "test", resp.GetHttpContentString())
 
 	originEnv := os.Getenv("HTTP_PROXY")
-	os.Setenv("HTTP_PROXY", ts.URL)
+	domain = strings.Replace(ts.URL, "http://", "", 1)
+	os.Setenv("HTTP_PROXY", fmt.Sprintf("http://someuser:somepassword@%s", domain))
 	resp, err = client.ProcessCommonRequest(request)
 	assert.Nil(t, err)
 	assert.Equal(t, 200, resp.GetHttpStatus())

+ 7 - 3
sdk/client.go

@@ -166,9 +166,7 @@ func (client *Client) GetConnectTimeout() time.Duration {
 	return client.connectTimeout
 }
 
-func (client *Client) getHttpProxy(scheme string) (*url.URL, error) {
-	var proxy *url.URL
-	var err error
+func (client *Client) getHttpProxy(scheme string) (proxy *url.URL, err error) {
 	if scheme == "https" {
 		if client.GetHttpsProxy() != "" {
 			proxy, err = url.Parse(client.httpsProxy)
@@ -461,6 +459,7 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
 	if err != nil {
 		return err
 	}
+
 	noProxy := client.getNoProxy(httpRequest.URL.Scheme)
 
 	var flag bool
@@ -485,6 +484,11 @@ func (client *Client) DoActionWithSigner(request requests.AcsRequest, response r
 
 	var httpResponse *http.Response
 	for retryTimes := 0; retryTimes <= client.config.MaxRetryTime; retryTimes++ {
+		if proxy != nil && proxy.User != nil{
+			if password, passwordSet := proxy.User.Password(); passwordSet {
+				httpRequest.SetBasicAuth(proxy.User.Username(), password)
+			}
+		}
 		debug("> %s %s %s", httpRequest.Method, httpRequest.URL.RequestURI(), httpRequest.Proto)
 		debug("> Host: %s", httpRequest.Host)
 		for key, value := range httpRequest.Header {

+ 2 - 1
sdk/client_test.go

@@ -210,13 +210,14 @@ func Test_DoActionWithProxy(t *testing.T) {
 	assert.Equal(t, url.Host, "127.0.0.1:9000")
 
 	// Test when setting https proxy, client has a high priority than environment variable
-	client.SetHttpsProxy("https://127.0.0.1:6666")
+	client.SetHttpsProxy("https://username:password@127.0.0.1:6666")
 	err = client.DoAction(request, response)
 	assert.Nil(t, err)
 	trans, _ = client.httpClient.Transport.(*http.Transport)
 	url, _ = trans.Proxy(nil)
 	assert.Equal(t, url.Scheme, "https")
 	assert.Equal(t, url.Host, "127.0.0.1:6666")
+	assert.Equal(t, url.User.Username(), "username")
 
 	client.Shutdown()
 	os.Setenv("https_proxy", envHttpsProxy)

+ 20 - 0
vendor/github.com/goji/httpauth/LICENSE

@@ -0,0 +1,20 @@
+Copyright (c) 2014 Carl Jackson (carl@avtok.com), Matt Silverlock (matt@eatsleeprepeat.net)
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 163 - 0
vendor/github.com/goji/httpauth/README.md

@@ -0,0 +1,163 @@
+# goji/httpauth [![GoDoc](https://godoc.org/github.com/goji/httpauth?status.svg)](https://godoc.org/github.com/goji/httpauth) [![Build Status](https://travis-ci.org/goji/httpauth.svg)](https://travis-ci.org/goji/httpauth)
+
+`httpauth` currently provides [HTTP Basic Authentication middleware](http://tools.ietf.org/html/rfc2617) for Go. It is compatible with Go's own `net/http`, [goji](https://goji.io), Gin & anything that speaks the `http.Handler` interface.
+
+## Example
+
+`httpauth` provides a `SimpleBasicAuth` function to get you up and running. Particularly ideal for development servers.
+
+Note that HTTP Basic Authentication credentials are sent over the wire "in the clear" (read: plaintext!) and therefore should not be considered a robust way to secure a HTTP server. If you're after that, you'll need to use SSL/TLS ("HTTPS") at a minimum.
+
+### Install It
+
+```sh
+$ go get github.com/goji/httpauth
+```
+
+### Goji v2
+
+#### Simple Usage
+
+The fastest and simplest way to get started using `httpauth` is to use the
+`SimpleBasicAuth` function.
+
+```go
+
+package main
+
+import(
+    "net/http"
+
+    "goji.io"
+)
+
+func main() {
+    mux := goji.NewMux()
+
+    mux.Use(httpauth.SimpleBasicAuth("dave", "somepassword"))
+    mux.Use(SomeOtherMiddleware)
+
+    // YourHandler now requires HTTP Basic Auth
+    mux.Handle(pat.Get("/some-route"), YourHandler))
+
+    log.Fatal(http.ListenAndServe("localhost:8000", mux))
+}
+```
+
+#### Advanced Usage
+
+For more control over the process, pass a `AuthOptions` struct to `BasicAuth` instead. This allows you to:
+
+* Configure the authentication realm.
+* Provide your own UnauthorizedHandler (anything that satisfies `http.Handler`) so you can return a better looking 401 page.
+* Define a custom authentication function, which is discussed in the next section.
+
+```go
+
+func main() {
+
+    authOpts := httpauth.AuthOptions{
+        Realm: "DevCo",
+        User: "dave",
+        Password: "plaintext!",
+        UnauthorizedHandler: myUnauthorizedHandler,
+    }
+
+    mux := goji.NewMux()
+
+    mux.Use(BasicAuth(authOpts))
+    mux.Use(SomeOtherMiddleware)
+
+    mux.Handle(pat.Get("/some-route"), YourHandler))
+
+    log.Fatal(http.ListenAndServe("localhost:8000", mux))
+}
+```
+
+#### Custom Authentication Function
+
+`httpauth` will accept a custom authentication function.
+Normally, you would not set `AuthOptions.User` nor `AuthOptions.Password` in this scenario.
+You would instead validate the given credentials against an external system such as a database.
+The contrived example below is for demonstration purposes only.
+
+```go
+func main() {
+
+    authOpts := httpauth.AuthOptions{
+        Realm: "DevCo",
+        AuthFunc: myAuthFunc,
+        UnauthorizedHandler: myUnauthorizedHandler,
+    }
+
+    mux := goji.NewMux()
+
+    mux.Use(BasicAuth(authOpts))
+    mux.Use(SomeOtherMiddleware)
+
+    mux.Handle(pat.Get("/some-route"), YourHandler))
+
+    log.Fatal(http.ListenAndServe("localhost:8000", mux))
+}
+
+// myAuthFunc is not secure.  It checks to see if the password is simply
+// the username repeated three times.
+func myAuthFunc(user, pass string, r *http.Request) bool {
+    return pass == strings.Repeat(user, 3)
+}
+```
+
+### gorilla/mux
+
+Since it's all `http.Handler`, `httpauth` works with [gorilla/mux](https://github.com/gorilla/mux) (and most other routers) as well:
+
+```go
+package main
+
+import (
+	"net/http"
+
+	"github.com/goji/httpauth"
+	"github.com/gorilla/mux"
+)
+
+func main() {
+	r := mux.NewRouter()
+
+	r.HandleFunc("/", YourHandler)
+	http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(r))
+
+	http.ListenAndServe(":7000", nil)
+}
+
+func YourHandler(w http.ResponseWriter, r *http.Request) {
+	w.Write([]byte("Gorilla!\n"))
+}
+```
+
+### net/http
+
+If you're using vanilla `net/http`:
+
+```go
+package main
+
+import(
+	"net/http"
+
+	"github.com/goji/httpauth"
+)
+
+func main() {
+	http.Handle("/", httpauth.SimpleBasicAuth("dave", "somepassword")(http.HandlerFunc(YourHandler)))
+	http.ListenAndServe(":7000", nil)
+}
+```
+
+## Contributing
+
+Send a pull request! Note that features on the (informal) roadmap include HTTP Digest Auth.
+
+## License
+
+MIT Licensed. See the LICENSE file for details.

+ 185 - 0
vendor/github.com/goji/httpauth/basic_auth.go

@@ -0,0 +1,185 @@
+package httpauth
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"crypto/subtle"
+	"encoding/base64"
+	"fmt"
+	"net/http"
+	"strings"
+)
+
+type basicAuth struct {
+	h    http.Handler
+	opts AuthOptions
+}
+
+// AuthOptions stores the configuration for HTTP Basic Authentication.
+//
+// A http.Handler may also be passed to UnauthorizedHandler to override the
+// default error handler if you wish to serve a custom template/response.
+type AuthOptions struct {
+	Realm               string
+	User                string
+	Password            string
+	AuthFunc            func(string, string, *http.Request) bool
+	UnauthorizedHandler http.Handler
+}
+
+// Satisfies the http.Handler interface for basicAuth.
+func (b basicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	// Check if we have a user-provided error handler, else set a default
+	if b.opts.UnauthorizedHandler == nil {
+		b.opts.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
+	}
+
+	// Check that the provided details match
+	if b.authenticate(r) == false {
+		b.requestAuth(w, r)
+		return
+	}
+
+	// Call the next handler on success.
+	b.h.ServeHTTP(w, r)
+}
+
+// authenticate retrieves and then validates the user:password combination provided in
+// the request header. Returns 'false' if the user has not successfully authenticated.
+func (b *basicAuth) authenticate(r *http.Request) bool {
+	const basicScheme string = "Basic "
+
+	if r == nil {
+		return false
+	}
+
+	// In simple mode, prevent authentication with empty credentials if User is
+	// not set. Allow empty passwords to support non-password use-cases.
+	if b.opts.AuthFunc == nil && b.opts.User == "" {
+		return false
+	}
+
+	// Confirm the request is sending Basic Authentication credentials.
+	auth := r.Header.Get("Authorization")
+	if !strings.HasPrefix(auth, basicScheme) {
+		return false
+	}
+
+	// Get the plain-text username and password from the request.
+	// The first six characters are skipped - e.g. "Basic ".
+	str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):])
+	if err != nil {
+		return false
+	}
+
+	// Split on the first ":" character only, with any subsequent colons assumed to be part
+	// of the password. Note that the RFC2617 standard does not place any limitations on
+	// allowable characters in the password.
+	creds := bytes.SplitN(str, []byte(":"), 2)
+
+	if len(creds) != 2 {
+		return false
+	}
+
+	givenUser := string(creds[0])
+	givenPass := string(creds[1])
+
+	// Default to Simple mode if no AuthFunc is defined.
+	if b.opts.AuthFunc == nil {
+		b.opts.AuthFunc = b.simpleBasicAuthFunc
+	}
+
+	return b.opts.AuthFunc(givenUser, givenPass, r)
+}
+
+// simpleBasicAuthFunc authenticates the supplied username and password against
+// the User and Password set in the Options struct.
+func (b *basicAuth) simpleBasicAuthFunc(user, pass string, r *http.Request) bool {
+	// Equalize lengths of supplied and required credentials
+	// by hashing them
+	givenUser := sha256.Sum256([]byte(user))
+	givenPass := sha256.Sum256([]byte(pass))
+	requiredUser := sha256.Sum256([]byte(b.opts.User))
+	requiredPass := sha256.Sum256([]byte(b.opts.Password))
+
+	// Compare the supplied credentials to those set in our options
+	if subtle.ConstantTimeCompare(givenUser[:], requiredUser[:]) == 1 &&
+		subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1 {
+		return true
+	}
+
+	return false
+}
+
+// Require authentication, and serve our error handler otherwise.
+func (b *basicAuth) requestAuth(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.opts.Realm))
+	b.opts.UnauthorizedHandler.ServeHTTP(w, r)
+}
+
+// defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response.
+func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
+	http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
+}
+
+// BasicAuth provides HTTP middleware for protecting URIs with HTTP Basic Authentication
+// as per RFC 2617. The server authenticates a user:password combination provided in the
+// "Authorization" HTTP header.
+//
+// Example:
+//
+//     package main
+//
+//     import(
+//            "net/http"
+//            "github.com/zenazn/goji"
+//            "github.com/goji/httpauth"
+//     )
+//
+//     func main() {
+//          basicOpts := httpauth.AuthOptions{
+//                      Realm: "Restricted",
+//                      User: "Dave",
+//                      Password: "ClearText",
+//                  }
+//
+//          goji.Use(httpauth.BasicAuth(basicOpts), SomeOtherMiddleware)
+//          goji.Get("/thing", myHandler)
+//  }
+//
+// Note: HTTP Basic Authentication credentials are sent in plain text, and therefore it does
+// not make for a wholly secure authentication mechanism. You should serve your content over
+// HTTPS to mitigate this, noting that "Basic Authentication" is meant to be just that: basic!
+func BasicAuth(o AuthOptions) func(http.Handler) http.Handler {
+	fn := func(h http.Handler) http.Handler {
+		return basicAuth{h, o}
+	}
+	return fn
+}
+
+// SimpleBasicAuth is a convenience wrapper around BasicAuth. It takes a user and password, and
+// returns a pre-configured BasicAuth handler using the "Restricted" realm and a default 401 handler.
+//
+// Example:
+//
+//     package main
+//
+//     import(
+//            "net/http"
+//            "github.com/zenazn/goji/web/httpauth"
+//     )
+//
+//     func main() {
+//
+//          goji.Use(httpauth.SimpleBasicAuth("dave", "somepassword"), SomeOtherMiddleware)
+//          goji.Get("/thing", myHandler)
+//      }
+//
+func SimpleBasicAuth(user, password string) func(http.Handler) http.Handler {
+	opts := AuthOptions{
+		Realm:    "Restricted",
+		User:     user,
+		Password: password,
+	}
+	return BasicAuth(opts)
+}