0 | 0 |
package basic
|
1 | 1 |
|
2 | 2 |
import (
|
|
3 |
"bytes"
|
3 | 4 |
"context"
|
4 | 5 |
"crypto/sha256"
|
5 | 6 |
"crypto/subtle"
|
|
12 | 13 |
httptransport "github.com/go-kit/kit/transport/http"
|
13 | 14 |
)
|
14 | 15 |
|
15 | |
// AuthError represents generic Authorization error
|
|
16 |
// AuthError represents an authoriation error.
|
16 | 17 |
type AuthError struct {
|
17 | 18 |
Realm string
|
18 | 19 |
}
|
19 | 20 |
|
20 | |
// StatusCode is an implemntation of StatusCoder interface in go-kit/http
|
|
21 |
// StatusCode is an iimplementation of the StatusCoder interface in go-kit/http.
|
21 | 22 |
func (AuthError) StatusCode() int {
|
22 | 23 |
return http.StatusUnauthorized
|
23 | 24 |
}
|
24 | 25 |
|
25 | |
// Error is an implemntation of Error interface
|
|
26 |
// Error is an implementation of the Error interface.
|
26 | 27 |
func (AuthError) Error() string {
|
27 | 28 |
return http.StatusText(http.StatusUnauthorized)
|
28 | 29 |
}
|
29 | 30 |
|
30 | |
// Headers is an implemntation of Headerer interface in go-kit/http
|
|
31 |
// Headers is an implemntation of the Headerer interface in go-kit/http.
|
31 | 32 |
func (e AuthError) Headers() http.Header {
|
32 | 33 |
return http.Header{
|
33 | 34 |
"Content-Type": []string{"text/plain; charset=utf-8"},
|
|
35 | 36 |
"WWW-Authenticate": []string{fmt.Sprintf(`Basic realm=%q`, e.Realm)}}
|
36 | 37 |
}
|
37 | 38 |
|
38 | |
func credsAreValid(givenUser, givenPass, requiredUser, requiredPass string) bool {
|
39 | |
// Equalize lengths of supplied and required credentials
|
40 | |
// by hashing them
|
41 | |
givenUserBytes := sha256.Sum256([]byte(givenUser))
|
42 | |
givenPassBytes := sha256.Sum256([]byte(givenPass))
|
43 | |
requiredUserBytes := sha256.Sum256([]byte(requiredUser))
|
44 | |
requiredPassBytes := sha256.Sum256([]byte(requiredPass))
|
45 | |
|
46 | |
// Compare the supplied credentials to those set in our options
|
47 | |
if subtle.ConstantTimeCompare(givenUserBytes[:], requiredUserBytes[:]) == 1 &&
|
48 | |
subtle.ConstantTimeCompare(givenPassBytes[:], requiredPassBytes[:]) == 1 {
|
49 | |
return true
|
50 | |
}
|
51 | |
|
52 | |
return false
|
53 | |
}
|
54 | |
|
55 | 39 |
// parseBasicAuth parses an HTTP Basic Authentication string.
|
56 | |
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ("Aladdin", "open sesame", true).
|
57 | |
func parseBasicAuth(auth string) (username, password string, ok bool) {
|
|
40 |
// "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==" returns ([]byte("Aladdin"), []byte("open sesame"), true).
|
|
41 |
func parseBasicAuth(auth string) (username, password []byte, ok bool) {
|
58 | 42 |
const prefix = "Basic "
|
59 | 43 |
if !strings.HasPrefix(auth, prefix) {
|
60 | 44 |
return
|
|
63 | 47 |
if err != nil {
|
64 | 48 |
return
|
65 | 49 |
}
|
66 | |
cs := string(c)
|
67 | |
s := strings.IndexByte(cs, ':')
|
|
50 |
|
|
51 |
s := bytes.IndexByte(c, ':')
|
68 | 52 |
if s < 0 {
|
69 | 53 |
return
|
70 | 54 |
}
|
71 | |
return cs[:s], cs[s+1:], true
|
|
55 |
return c[:s], c[s+1:], true
|
72 | 56 |
}
|
73 | 57 |
|
74 | |
// AuthMiddleware returns a Basic Authentication middleware for a particular user and password
|
|
58 |
// AuthMiddleware returns a Basic Authentication middleware for a particular user and password.
|
75 | 59 |
func AuthMiddleware(requiredUser, requiredPassword, realm string) endpoint.Middleware {
|
|
60 |
requiredUserBytes := sha256.Sum256([]byte(requiredUser))
|
|
61 |
requiredPassBytes := sha256.Sum256([]byte(requiredPassword))
|
|
62 |
|
76 | 63 |
return func(next endpoint.Endpoint) endpoint.Endpoint {
|
77 | 64 |
return func(ctx context.Context, request interface{}) (interface{}, error) {
|
78 | 65 |
auth := ctx.Value(httptransport.ContextKeyRequestAuthorization).(string)
|
|
81 | 68 |
return nil, AuthError{realm}
|
82 | 69 |
}
|
83 | 70 |
|
84 | |
if !credsAreValid(givenUser, givenPass, requiredUser, requiredPassword) {
|
|
71 |
// Equalize lengths of supplied and required credentials by hashing them.
|
|
72 |
givenUserBytes := sha256.Sum256(givenUser)
|
|
73 |
givenPassBytes := sha256.Sum256(givenPass)
|
|
74 |
|
|
75 |
// Compare the supplied credentials to those set in our options.
|
|
76 |
if subtle.ConstantTimeCompare(givenUserBytes[:], requiredUserBytes[:]) == 0 ||
|
|
77 |
subtle.ConstantTimeCompare(givenPassBytes[:], requiredPassBytes[:]) == 0 {
|
85 | 78 |
return nil, AuthError{realm}
|
86 | 79 |
}
|
87 | 80 |
|