security.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. """Kazoo Security"""
  2. from base64 import b64encode
  3. from collections import namedtuple
  4. import hashlib
  5. # Represents a Zookeeper ID and ACL object
  6. Id = namedtuple('Id', 'scheme id')
  7. class ACL(namedtuple('ACL', 'perms id')):
  8. """An ACL for a Zookeeper Node
  9. An ACL object is created by using an :class:`Id` object along with
  10. a :class:`Permissions` setting. For convenience,
  11. :meth:`make_digest_acl` should be used to create an ACL object with
  12. the desired scheme, id, and permissions.
  13. """
  14. @property
  15. def acl_list(self):
  16. perms = []
  17. if self.perms & Permissions.ALL == Permissions.ALL:
  18. perms.append('ALL')
  19. return perms
  20. if self.perms & Permissions.READ == Permissions.READ:
  21. perms.append('READ')
  22. if self.perms & Permissions.WRITE == Permissions.WRITE:
  23. perms.append('WRITE')
  24. if self.perms & Permissions.CREATE == Permissions.CREATE:
  25. perms.append('CREATE')
  26. if self.perms & Permissions.DELETE == Permissions.DELETE:
  27. perms.append('DELETE')
  28. if self.perms & Permissions.ADMIN == Permissions.ADMIN:
  29. perms.append('ADMIN')
  30. return perms
  31. def __repr__(self):
  32. return 'ACL(perms=%r, acl_list=%s, id=%r)' % (
  33. self.perms, self.acl_list, self.id)
  34. class Permissions(object):
  35. READ = 1
  36. WRITE = 2
  37. CREATE = 4
  38. DELETE = 8
  39. ADMIN = 16
  40. ALL = 31
  41. # Shortcuts for common Ids
  42. ANYONE_ID_UNSAFE = Id('world', 'anyone')
  43. AUTH_IDS = Id('auth', '')
  44. # Shortcuts for common ACLs
  45. OPEN_ACL_UNSAFE = [ACL(Permissions.ALL, ANYONE_ID_UNSAFE)]
  46. CREATOR_ALL_ACL = [ACL(Permissions.ALL, AUTH_IDS)]
  47. READ_ACL_UNSAFE = [ACL(Permissions.READ, ANYONE_ID_UNSAFE)]
  48. def make_digest_acl_credential(username, password):
  49. """Create a SHA1 digest credential.
  50. .. note::
  51. This function uses UTF-8 to encode non-ASCII codepoints,
  52. whereas ZooKeeper uses the "default locale" for decoding. It
  53. may be a good idea to start the JVM with `-Dfile.encoding=UTF-8`
  54. in non-UTF-8 locales.
  55. See: https://github.com/python-zk/kazoo/pull/584
  56. """
  57. credential = username.encode('utf-8') + b":" + password.encode('utf-8')
  58. cred_hash = b64encode(hashlib.sha1(credential).digest()).strip()
  59. return username + ":" + cred_hash.decode('utf-8')
  60. def make_acl(scheme, credential, read=False, write=False,
  61. create=False, delete=False, admin=False, all=False):
  62. """Given a scheme and credential, return an :class:`ACL` object
  63. appropriate for use with Kazoo.
  64. :param scheme: The scheme to use. I.e. `digest`.
  65. :param credential:
  66. A colon separated username, password. The password should be
  67. hashed with the `scheme` specified. The
  68. :meth:`make_digest_acl_credential` method will create and
  69. return a credential appropriate for use with the `digest`
  70. scheme.
  71. :param write: Write permission.
  72. :type write: bool
  73. :param create: Create permission.
  74. :type create: bool
  75. :param delete: Delete permission.
  76. :type delete: bool
  77. :param admin: Admin permission.
  78. :type admin: bool
  79. :param all: All permissions.
  80. :type all: bool
  81. :rtype: :class:`ACL`
  82. """
  83. if all:
  84. permissions = Permissions.ALL
  85. else:
  86. permissions = 0
  87. if read:
  88. permissions |= Permissions.READ
  89. if write:
  90. permissions |= Permissions.WRITE
  91. if create:
  92. permissions |= Permissions.CREATE
  93. if delete:
  94. permissions |= Permissions.DELETE
  95. if admin:
  96. permissions |= Permissions.ADMIN
  97. return ACL(permissions, Id(scheme, credential))
  98. def make_digest_acl(username, password, read=False, write=False,
  99. create=False, delete=False, admin=False, all=False):
  100. """Create a digest ACL for Zookeeper with the given permissions
  101. This method combines :meth:`make_digest_acl_credential` and
  102. :meth:`make_acl` to create an :class:`ACL` object appropriate for
  103. use with Kazoo's ACL methods.
  104. :param username: Username to use for the ACL.
  105. :param password: A plain-text password to hash.
  106. :param write: Write permission.
  107. :type write: bool
  108. :param create: Create permission.
  109. :type create: bool
  110. :param delete: Delete permission.
  111. :type delete: bool
  112. :param admin: Admin permission.
  113. :type admin: bool
  114. :param all: All permissions.
  115. :type all: bool
  116. :rtype: :class:`ACL`
  117. """
  118. cred = make_digest_acl_credential(username, password)
  119. return make_acl("digest", cred, read=read, write=write, create=create,
  120. delete=delete, admin=admin, all=all)