github.com/coreos/goproxy@v0.0.0-20190513173959-f8dc2d7ba04e/ext/auth/basic.go (about)

     1  package auth
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"strings"
     9  
    10  	"github.com/elazarl/goproxy"
    11  )
    12  
    13  var unauthorizedMsg = []byte("407 Proxy Authentication Required")
    14  
    15  func BasicUnauthorized(req *http.Request, realm string) *http.Response {
    16  	// TODO(elazar): verify realm is well formed
    17  	return &http.Response{
    18  		StatusCode:    407,
    19  		ProtoMajor:    1,
    20  		ProtoMinor:    1,
    21  		Request:       req,
    22  		Header:        http.Header{"Proxy-Authenticate": []string{"Basic realm=" + realm}},
    23  		Body:          ioutil.NopCloser(bytes.NewBuffer(unauthorizedMsg)),
    24  		ContentLength: int64(len(unauthorizedMsg)),
    25  	}
    26  }
    27  
    28  var proxyAuthorizationHeader = "Proxy-Authorization"
    29  
    30  func auth(req *http.Request, f func(user, passwd string) bool) bool {
    31  	authheader := strings.SplitN(req.Header.Get(proxyAuthorizationHeader), " ", 2)
    32  	req.Header.Del(proxyAuthorizationHeader)
    33  	if len(authheader) != 2 || authheader[0] != "Basic" {
    34  		return false
    35  	}
    36  	userpassraw, err := base64.StdEncoding.DecodeString(authheader[1])
    37  	if err != nil {
    38  		return false
    39  	}
    40  	userpass := strings.SplitN(string(userpassraw), ":", 2)
    41  	if len(userpass) != 2 {
    42  		return false
    43  	}
    44  	return f(userpass[0], userpass[1])
    45  }
    46  
    47  // Basic returns a basic HTTP authentication handler for requests
    48  //
    49  // You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities
    50  func Basic(realm string, f func(user, passwd string) bool) goproxy.ReqHandler {
    51  	return goproxy.FuncReqHandler(func(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) {
    52  		if !auth(req, f) {
    53  			return nil, BasicUnauthorized(req, realm)
    54  		}
    55  		return req, nil
    56  	})
    57  }
    58  
    59  // BasicConnect returns a basic HTTP authentication handler for CONNECT requests
    60  //
    61  // You probably want to use auth.ProxyBasic(proxy) to enable authentication for all proxy activities
    62  func BasicConnect(realm string, f func(user, passwd string) bool) goproxy.HttpsHandler {
    63  	return goproxy.FuncHttpsHandler(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
    64  		if !auth(ctx.Req, f) {
    65  			ctx.Resp = BasicUnauthorized(ctx.Req, realm)
    66  			return goproxy.RejectConnect, host
    67  		}
    68  		return goproxy.OkConnect, host
    69  	})
    70  }
    71  
    72  // ProxyBasic will force HTTP authentication before any request to the proxy is processed
    73  func ProxyBasic(proxy *goproxy.ProxyHttpServer, realm string, f func(user, passwd string) bool) {
    74  	proxy.OnRequest().Do(Basic(realm, f))
    75  	proxy.OnRequest().HandleConnect(BasicConnect(realm, f))
    76  }