...

Source file src/github.com/go-kit/kit/auth/basic/middleware.go

Documentation: github.com/go-kit/kit/auth/basic

     1  package basic
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/sha256"
     7  	"crypto/subtle"
     8  	"encoding/base64"
     9  	"fmt"
    10  	"net/http"
    11  	"strings"
    12  
    13  	"github.com/go-kit/kit/endpoint"
    14  	httptransport "github.com/go-kit/kit/transport/http"
    15  )
    16  
    17  // AuthError represents an authorization error.
    18  type AuthError struct {
    19  	Realm string
    20  }
    21  
    22  // StatusCode is an implementation of the StatusCoder interface in go-kit/http.
    23  func (AuthError) StatusCode() int {
    24  	return http.StatusUnauthorized
    25  }
    26  
    27  // Error is an implementation of the Error interface.
    28  func (AuthError) Error() string {
    29  	return http.StatusText(http.StatusUnauthorized)
    30  }
    31  
    32  // Headers is an implementation of the Headerer interface in go-kit/http.
    33  func (e AuthError) Headers() http.Header {
    34  	return http.Header{
    35  		"Content-Type":           []string{"text/plain; charset=utf-8"},
    36  		"X-Content-Type-Options": []string{"nosniff"},
    37  		"WWW-Authenticate":       []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)},
    38  	}
    39  }
    40  
    41  // parseBasicAuth parses an HTTP Basic Authentication string.
    42  // "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ([]byte("Aladdin"), []byte("open sesame"), true).
    43  func parseBasicAuth(auth string) (username, password []byte, ok bool) {
    44  	const prefix = "Basic "
    45  	if !strings.HasPrefix(auth, prefix) {
    46  		return
    47  	}
    48  	c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
    49  	if err != nil {
    50  		return
    51  	}
    52  
    53  	s := bytes.IndexByte(c, ':')
    54  	if s < 0 {
    55  		return
    56  	}
    57  	return c[:s], c[s+1:], true
    58  }
    59  
    60  // Returns a hash of a given slice.
    61  func toHashSlice(s []byte) []byte {
    62  	hash := sha256.Sum256(s)
    63  	return hash[:]
    64  }
    65  
    66  // AuthMiddleware returns a Basic Authentication middleware for a particular user and password.
    67  func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware {
    68  	requiredUserBytes := toHashSlice([]byte(requiredUser))
    69  	requiredPasswordBytes := toHashSlice([]byte(requiredPassword))
    70  
    71  	return func(next endpoint.Endpoint) endpoint.Endpoint {
    72  		return func(ctx context.Context, request interface{}) (interface{}, error) {
    73  			auth, ok := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)
    74  			if !ok {
    75  				return nil, AuthError{realm}
    76  			}
    77  
    78  			givenUser, givenPassword, ok := parseBasicAuth(auth)
    79  			if !ok {
    80  				return nil, AuthError{realm}
    81  			}
    82  
    83  			givenUserBytes := toHashSlice(givenUser)
    84  			givenPasswordBytes := toHashSlice(givenPassword)
    85  
    86  			if subtle.ConstantTimeCompare(givenUserBytes, requiredUserBytes) == 0 ||
    87  				subtle.ConstantTimeCompare(givenPasswordBytes, requiredPasswordBytes) == 0 {
    88  				return nil, AuthError{realm}
    89  			}
    90  
    91  			return next(ctx, request)
    92  		}
    93  	}
    94  }
    95  

View as plain text