auth.go 3.7 KB

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