util.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package zk
  2. import (
  3. "crypto/sha1"
  4. "encoding/base64"
  5. "fmt"
  6. "math/rand"
  7. "strconv"
  8. "strings"
  9. "unicode/utf8"
  10. )
  11. // AuthACL produces an ACL list containing a single ACL which uses the
  12. // provided permissions, with the scheme "auth", and ID "", which is used
  13. // by ZooKeeper to represent any authenticated user.
  14. func AuthACL(perms int32) []ACL {
  15. return []ACL{{perms, "auth", ""}}
  16. }
  17. // WorldACL produces an ACL list containing a single ACL which uses the
  18. // provided permissions, with the scheme "world", and ID "anyone", which
  19. // is used by ZooKeeper to represent any user at all.
  20. func WorldACL(perms int32) []ACL {
  21. return []ACL{{perms, "world", "anyone"}}
  22. }
  23. func DigestACL(perms int32, user, password string) []ACL {
  24. userPass := []byte(fmt.Sprintf("%s:%s", user, password))
  25. h := sha1.New()
  26. if n, err := h.Write(userPass); err != nil || n != len(userPass) {
  27. panic("SHA1 failed")
  28. }
  29. digest := base64.StdEncoding.EncodeToString(h.Sum(nil))
  30. return []ACL{{perms, "digest", fmt.Sprintf("%s:%s", user, digest)}}
  31. }
  32. // FormatServers takes a slice of addresses, and makes sure they are in a format
  33. // that resembles <addr>:<port>. If the server has no port provided, the
  34. // DefaultPort constant is added to the end.
  35. func FormatServers(servers []string) []string {
  36. for i := range servers {
  37. if !strings.Contains(servers[i], ":") {
  38. servers[i] = servers[i] + ":" + strconv.Itoa(DefaultPort)
  39. }
  40. }
  41. return servers
  42. }
  43. // stringShuffle performs a Fisher-Yates shuffle on a slice of strings
  44. func stringShuffle(s []string) {
  45. for i := len(s) - 1; i > 0; i-- {
  46. j := rand.Intn(i + 1)
  47. s[i], s[j] = s[j], s[i]
  48. }
  49. }
  50. // validatePath will make sure a path is valid before sending the request
  51. func validatePath(path string, isSequential bool) error {
  52. if path == "" {
  53. return ErrInvalidPath
  54. }
  55. if path[0] != '/' {
  56. return ErrInvalidPath
  57. }
  58. n := len(path)
  59. if n == 1 {
  60. // path is just the root
  61. return nil
  62. }
  63. if !isSequential && path[n-1] == '/' {
  64. return ErrInvalidPath
  65. }
  66. // Start at rune 1 since we already know that the first character is
  67. // a '/'.
  68. for i, w := 1, 0; i < n; i += w {
  69. r, width := utf8.DecodeRuneInString(path[i:])
  70. switch {
  71. case r == '\u0000':
  72. return ErrInvalidPath
  73. case r == '/':
  74. last, _ := utf8.DecodeLastRuneInString(path[:i])
  75. if last == '/' {
  76. return ErrInvalidPath
  77. }
  78. case r == '.':
  79. last, lastWidth := utf8.DecodeLastRuneInString(path[:i])
  80. // Check for double dot
  81. if last == '.' {
  82. last, _ = utf8.DecodeLastRuneInString(path[:i-lastWidth])
  83. }
  84. if last == '/' {
  85. if i+1 == n {
  86. return ErrInvalidPath
  87. }
  88. next, _ := utf8.DecodeRuneInString(path[i+w:])
  89. if next == '/' {
  90. return ErrInvalidPath
  91. }
  92. }
  93. case r >= '\u0000' && r <= '\u001f',
  94. r >= '\u007f' && r <= '\u009f',
  95. r >= '\uf000' && r <= '\uf8ff',
  96. r >= '\ufff0' && r < '\uffff':
  97. return ErrInvalidPath
  98. }
  99. w = width
  100. }
  101. return nil
  102. }