auth.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  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. "strconv"
  9. )
  10. // AuthUserKey is the cookie name for user credential in basic auth
  11. const AuthUserKey = "user"
  12. type (
  13. // Accounts defines a key/value for user/pass list of authorized logins
  14. Accounts map[string]string
  15. authPair struct {
  16. Value string
  17. User string
  18. }
  19. authPairs []authPair
  20. )
  21. func (a authPairs) searchCredential(authValue string) (string, bool) {
  22. if len(authValue) == 0 {
  23. return "", false
  24. }
  25. for _, pair := range a {
  26. if pair.Value == authValue {
  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.Request.Header.Get("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(401)
  49. } else {
  50. // The user credentials was found, set user's id to key AuthUserKey in this context, the userId can be read later using
  51. // c.MustGet(gin.AuthUserKey)
  52. c.Set(AuthUserKey, user)
  53. }
  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. assert1(len(accounts) > 0, "Empty list of authorized credentials")
  63. pairs := make(authPairs, 0, len(accounts))
  64. for user, password := range accounts {
  65. assert1(len(user) > 0, "User can not be empty")
  66. value := authorizationHeader(user, password)
  67. pairs = append(pairs, authPair{
  68. Value: value,
  69. User: user,
  70. })
  71. }
  72. return pairs
  73. }
  74. func authorizationHeader(user, password string) string {
  75. base := user + ":" + password
  76. return "Basic " + base64.StdEncoding.EncodeToString([]byte(base))
  77. }
  78. func secureCompare(given, actual string) bool {
  79. if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 {
  80. return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1
  81. }
  82. /* Securely compare actual to itself to keep constant time, but always return false */
  83. return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false
  84. }