gin-gonic / gin
1
// Copyright 2014 Manu Martinez-Almeida.  All rights reserved.
2
// Use of this source code is governed by a MIT style
3
// license that can be found in the LICENSE file.
4

5
package gin
6

7
import (
8
	"crypto/subtle"
9
	"encoding/base64"
10
	"net/http"
11
	"strconv"
12

13
	"github.com/gin-gonic/gin/internal/bytesconv"
14
)
15

16
// AuthUserKey is the cookie name for user credential in basic auth.
17
const AuthUserKey = "user"
18

19
// Accounts defines a key/value for user/pass list of authorized logins.
20
type Accounts map[string]string
21

22
type authPair struct {
23
	value string
24
	user  string
25
}
26

27
type authPairs []authPair
28

29
func (a authPairs) searchCredential(authValue string) (string, bool) {
30 151
	if authValue == "" {
31 151
		return "", false
32
	}
33
	for _, pair := range a {
34 151
		if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
35 151
			return pair.user, true
36
		}
37
	}
38 151
	return "", false
39
}
40

41
// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
42
// the key is the user name and the value is the password, as well as the name of the Realm.
43
// If the realm is empty, "Authorization Required" will be used by default.
44
// (see http://tools.ietf.org/html/rfc2617#section-1.2)
45
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
46 151
	if realm == "" {
47 151
		realm = "Authorization Required"
48
	}
49 151
	realm = "Basic realm=" + strconv.Quote(realm)
50 151
	pairs := processAccounts(accounts)
51 151
	return func(c *Context) {
52
		// Search user in the slice of allowed credentials
53 151
		user, found := pairs.searchCredential(c.requestHeader("Authorization"))
54 151
		if !found {
55
			// Credentials doesn't match, we return 401 and abort handlers chain.
56 151
			c.Header("WWW-Authenticate", realm)
57 151
			c.AbortWithStatus(http.StatusUnauthorized)
58 151
			return
59
		}
60

61
		// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
62
		// c.MustGet(gin.AuthUserKey).
63 151
		c.Set(AuthUserKey, user)
64
	}
65
}
66

67
// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
68
// the key is the user name and the value is the password.
69
func BasicAuth(accounts Accounts) HandlerFunc {
70 151
	return BasicAuthForRealm(accounts, "")
71
}
72

73
func processAccounts(accounts Accounts) authPairs {
74 151
	length := len(accounts)
75 151
	assert1(length > 0, "Empty list of authorized credentials")
76 151
	pairs := make(authPairs, 0, length)
77
	for user, password := range accounts {
78 151
		assert1(user != "", "User can not be empty")
79 151
		value := authorizationHeader(user, password)
80 151
		pairs = append(pairs, authPair{
81 151
			value: value,
82 151
			user:  user,
83 151
		})
84
	}
85 151
	return pairs
86
}
87

88
func authorizationHeader(user, password string) string {
89 151
	base := user + ":" + password
90 151
	return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base))
91
}

Read our documentation on viewing source code .

Loading