auth.go (2804B)
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 if authValue == "" { 31 return "", false 32 } 33 for _, pair := range a { 34 if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 { 35 return pair.user, true 36 } 37 } 38 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 if realm == "" { 47 realm = "Authorization Required" 48 } 49 realm = "Basic realm=" + strconv.Quote(realm) 50 pairs := processAccounts(accounts) 51 return func(c *Context) { 52 // Search user in the slice of allowed credentials 53 user, found := pairs.searchCredential(c.requestHeader("Authorization")) 54 if !found { 55 // Credentials doesn't match, we return 401 and abort handlers chain. 56 c.Header("WWW-Authenticate", realm) 57 c.AbortWithStatus(http.StatusUnauthorized) 58 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 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 return BasicAuthForRealm(accounts, "") 71 } 72 73 func processAccounts(accounts Accounts) authPairs { 74 length := len(accounts) 75 assert1(length > 0, "Empty list of authorized credentials") 76 pairs := make(authPairs, 0, length) 77 for user, password := range accounts { 78 assert1(user != "", "User can not be empty") 79 value := authorizationHeader(user, password) 80 pairs = append(pairs, authPair{ 81 value: value, 82 user: user, 83 }) 84 } 85 return pairs 86 } 87 88 func authorizationHeader(user, password string) string { 89 base := user + ":" + password 90 return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base)) 91 }