use github.com/goji/httpauth instead

This commit is contained in:
Yu Cao
2018-11-01 17:51:24 -04:00
parent b2a664c27b
commit 308f923e48
5 changed files with 218 additions and 77 deletions

View File

@@ -12,6 +12,7 @@ import (
"strings"
"time"
"github.com/goji/httpauth"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
@@ -207,12 +208,6 @@ func appMain(flags appFlags) {
setLogFormatter(flags.logPrefix)
runtime.SetBlockProfileRate(flags.blockProfileRate)
if flags.basicAuth {
log.Infof("Basic authentication enabled")
} else {
log.Infof("Basic authentication disabled")
}
traceCloser := tracing.NewFromEnv(fmt.Sprintf("scope-%s", flags.serviceName))
defer traceCloser.Close()
@@ -306,11 +301,10 @@ func appMain(flags appFlags) {
}
if flags.basicAuth {
handler = BasicAuthentication{
Realm: "Restricted",
User: flags.username,
Password: flags.password,
}.Wrap(handler)
log.Infof("Basic authentication enabled")
handler = httpauth.SimpleBasicAuth(flags.username, flags.password)(handler)
} else {
log.Infof("Basic authentication disabled")
}
server := &graceful.Server{

View File

@@ -1,66 +0,0 @@
package main
import (
"bytes"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"fmt"
"net/http"
"strings"
)
// BasicAuthentication middleware authenticate http request
type BasicAuthentication struct {
Realm string
User string
Password string
}
// Wrap implements Middleware
func (b BasicAuthentication) Wrap(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authenticated := b.authenticate(r)
if !authenticated {
b.requestAuth(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
func (b *BasicAuthentication) authenticate(r *http.Request) bool {
const basicScheme string = "Basic "
// Confirm the request is sending Basic Authentication credentials.
auth := r.Header.Get("Authorization")
if !strings.HasPrefix(auth, basicScheme) {
return false
}
str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):])
if err != nil {
return false
}
creds := bytes.SplitN(str, []byte(":"), 2)
if len(creds) != 2 {
return false
}
givenUser := sha256.Sum256([]byte(string(creds[0])))
givenPass := sha256.Sum256([]byte(string(creds[1])))
requiredUser := sha256.Sum256([]byte(b.User))
requiredPass := sha256.Sum256([]byte(b.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
}
func (b *BasicAuthentication) requestAuth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.Realm))
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}

20
vendor/github.com/goji/httpauth/LICENSE generated vendored Normal file
View File

@@ -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.

185
vendor/github.com/goji/httpauth/basic_auth.go generated vendored Normal file
View File

@@ -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)
}

8
vendor/manifest vendored
View File

@@ -871,6 +871,14 @@
"path": "types",
"notests": true
},
{
"importpath": "github.com/goji/httpauth",
"repository": "https://github.com/goji/httpauth",
"vcs": "git",
"revision": "2da839ab0f4df05a6db5eb277995589dadbd4fb9",
"branch": "master",
"notests": true
},
{
"importpath": "github.com/golang/glog",
"repository": "https://github.com/golang/glog",