auth.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  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. package gin
  5. import (
  6. "crypto/subtle"
  7. "encoding/base64"
  8. "net/http"
  9. "strconv"
  10. "github.com/gin-gonic/gin/internal/bytesconv"
  11. )
  12. // AuthUserKey is the cookie name for user credential in basic auth.
  13. const AuthUserKey = "user"
  14. // Accounts defines a key/value for user/pass list of authorized logins.
  15. type Accounts map[string]string
  16. type authPair struct {
  17. value string
  18. user string
  19. }
  20. type authPairs []authPair
  21. func (a authPairs) searchCredential(authValue string) (string, bool) {
  22. if authValue == "" {
  23. return "", false
  24. }
  25. for _, pair := range a {
  26. if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 {
  27. return pair.user, true
  28. }
  29. }
  30. return "", false
  31. }
  32. // BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
  33. // the key is the user name and the value is the password, as well as the name of the Realm.
  34. // If the realm is empty, "Authorization Required" will be used by default.
  35. // (see http://tools.ietf.org/html/rfc2617#section-1.2)
  36. func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
  37. if realm == "" {
  38. realm = "Authorization Required"
  39. }
  40. realm = "Basic realm=" + strconv.Quote(realm)
  41. pairs := processAccounts(accounts)
  42. return func(c *Context) {
  43. // Search user in the slice of allowed credentials
  44. user, found := pairs.searchCredential(c.requestHeader("Authorization"))
  45. if !found {
  46. // Credentials doesn't match, we return 401 and abort handlers chain.
  47. c.Header("WWW-Authenticate", realm)
  48. c.AbortWithStatus(http.StatusUnauthorized)
  49. return
  50. }
  51. // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
  52. // c.MustGet(gin.AuthUserKey).
  53. c.Set(AuthUserKey, user)
  54. }
  55. }
  56. // BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
  57. // the key is the user name and the value is the password.
  58. func BasicAuth(accounts Accounts) HandlerFunc {
  59. return BasicAuthForRealm(accounts, "")
  60. }
  61. func processAccounts(accounts Accounts) authPairs {
  62. length := len(accounts)
  63. assert1(length > 0, "Empty list of authorized credentials")
  64. pairs := make(authPairs, 0, length)
  65. for user, password := range accounts {
  66. assert1(user != "", "User can not be empty")
  67. value := authorizationHeader(user, password)
  68. pairs = append(pairs, authPair{
  69. value: value,
  70. user: user,
  71. })
  72. }
  73. return pairs
  74. }
  75. func authorizationHeader(user, password string) string {
  76. base := user + ":" + password
  77. return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base))
  78. }