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 101
	if authValue == "" {
31 101
		return "", false
32
	}
33
	for _, pair := range a {
34 101
		if subtle.ConstantTimeCompare([]byte(pair.value), []byte(authValue)) == 1 {
35 101
			return pair.user, true
36
		}
37
	}
38 101
	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 101
	if realm == "" {
47 101
		realm = "Authorization Required"
48
	}
49 101
	realm = "Basic realm=" + strconv.Quote(realm)
50 101
	pairs := processAccounts(accounts)
51 101
	return func(c *Context) {
52
		// Search user in the slice of allowed credentials
53 101
		user, found := pairs.searchCredential(c.requestHeader("Authorization"))
54 101
		if !found {
55
			// Credentials doesn't match, we return 401 and abort handlers chain.
56 101
			c.Header("WWW-Authenticate", realm)
57 101
			c.AbortWithStatus(http.StatusUnauthorized)
58 101
			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 101
		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 101
	return BasicAuthForRealm(accounts, "")
71
}
72

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

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

Read our documentation on viewing source code .

Loading