__init__.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. """
  2. Creates permissions for all installed apps that need permissions.
  3. """
  4. from __future__ import unicode_literals
  5. import getpass
  6. import unicodedata
  7. from django.apps import apps
  8. from django.contrib.auth import models as auth_app, get_permission_codename
  9. from django.core import exceptions
  10. from django.core.management.base import CommandError
  11. from django.db import DEFAULT_DB_ALIAS, router
  12. from django.db.models import signals
  13. from django.utils.encoding import DEFAULT_LOCALE_ENCODING
  14. from django.utils import six
  15. def _get_all_permissions(opts, ctype):
  16. """
  17. Returns (codename, name) for all permissions in the given opts.
  18. """
  19. builtin = _get_builtin_permissions(opts)
  20. custom = list(opts.permissions)
  21. _check_permission_clashing(custom, builtin, ctype)
  22. return builtin + custom
  23. def _get_builtin_permissions(opts):
  24. """
  25. Returns (codename, name) for all autogenerated permissions.
  26. By default, this is ('add', 'change', 'delete')
  27. """
  28. perms = []
  29. for action in opts.default_permissions:
  30. perms.append((get_permission_codename(action, opts),
  31. 'Can %s %s' % (action, opts.verbose_name_raw)))
  32. return perms
  33. def _check_permission_clashing(custom, builtin, ctype):
  34. """
  35. Check that permissions for a model do not clash. Raises CommandError if
  36. there are duplicate permissions.
  37. """
  38. pool = set()
  39. builtin_codenames = set(p[0] for p in builtin)
  40. for codename, _name in custom:
  41. if codename in pool:
  42. raise CommandError(
  43. "The permission codename '%s' is duplicated for model '%s.%s'." %
  44. (codename, ctype.app_label, ctype.model_class().__name__))
  45. elif codename in builtin_codenames:
  46. raise CommandError(
  47. "The permission codename '%s' clashes with a builtin permission "
  48. "for model '%s.%s'." %
  49. (codename, ctype.app_label, ctype.model_class().__name__))
  50. pool.add(codename)
  51. def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs):
  52. if not app_config.models_module:
  53. return
  54. try:
  55. Permission = apps.get_model('auth', 'Permission')
  56. except LookupError:
  57. return
  58. if not router.allow_migrate(using, Permission):
  59. return
  60. from django.contrib.contenttypes.models import ContentType
  61. # This will hold the permissions we're looking for as
  62. # (content_type, (codename, name))
  63. searched_perms = list()
  64. # The codenames and ctypes that should exist.
  65. ctypes = set()
  66. for klass in app_config.get_models():
  67. # Force looking up the content types in the current database
  68. # before creating foreign keys to them.
  69. ctype = ContentType.objects.db_manager(using).get_for_model(klass)
  70. ctypes.add(ctype)
  71. for perm in _get_all_permissions(klass._meta, ctype):
  72. searched_perms.append((ctype, perm))
  73. # Find all the Permissions that have a content_type for a model we're
  74. # looking for. We don't need to check for codenames since we already have
  75. # a list of the ones we're going to create.
  76. all_perms = set(Permission.objects.using(using).filter(
  77. content_type__in=ctypes,
  78. ).values_list(
  79. "content_type", "codename"
  80. ))
  81. perms = [
  82. Permission(codename=codename, name=name, content_type=ct)
  83. for ct, (codename, name) in searched_perms
  84. if (ct.pk, codename) not in all_perms
  85. ]
  86. # Validate the permissions before bulk_creation to avoid cryptic
  87. # database error when the verbose_name is longer than 50 characters
  88. permission_name_max_length = Permission._meta.get_field('name').max_length
  89. verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix
  90. for perm in perms:
  91. if len(perm.name) > permission_name_max_length:
  92. raise exceptions.ValidationError(
  93. "The verbose_name of %s is longer than %s characters" % (
  94. perm.content_type,
  95. verbose_name_max_length,
  96. )
  97. )
  98. Permission.objects.using(using).bulk_create(perms)
  99. if verbosity >= 2:
  100. for perm in perms:
  101. print("Adding permission '%s'" % perm)
  102. def get_system_username():
  103. """
  104. Try to determine the current system user's username.
  105. :returns: The username as a unicode string, or an empty string if the
  106. username could not be determined.
  107. """
  108. try:
  109. result = getpass.getuser()
  110. except (ImportError, KeyError):
  111. # KeyError will be raised by os.getpwuid() (called by getuser())
  112. # if there is no corresponding entry in the /etc/passwd file
  113. # (a very restricted chroot environment, for example).
  114. return ''
  115. if six.PY2:
  116. try:
  117. result = result.decode(DEFAULT_LOCALE_ENCODING)
  118. except UnicodeDecodeError:
  119. # UnicodeDecodeError - preventive treatment for non-latin Windows.
  120. return ''
  121. return result
  122. def get_default_username(check_db=True):
  123. """
  124. Try to determine the current system user's username to use as a default.
  125. :param check_db: If ``True``, requires that the username does not match an
  126. existing ``auth.User`` (otherwise returns an empty string).
  127. :returns: The username, or an empty string if no username can be
  128. determined.
  129. """
  130. # If the User model has been swapped out, we can't make any assumptions
  131. # about the default user name.
  132. if auth_app.User._meta.swapped:
  133. return ''
  134. default_username = get_system_username()
  135. try:
  136. default_username = (unicodedata.normalize('NFKD', default_username)
  137. .encode('ascii', 'ignore').decode('ascii')
  138. .replace(' ', '').lower())
  139. except UnicodeDecodeError:
  140. return ''
  141. # Run the username validator
  142. try:
  143. auth_app.User._meta.get_field('username').run_validators(default_username)
  144. except exceptions.ValidationError:
  145. return ''
  146. # Don't return the default username if it is already taken.
  147. if check_db and default_username:
  148. try:
  149. auth_app.User._default_manager.get(username=default_username)
  150. except auth_app.User.DoesNotExist:
  151. pass
  152. else:
  153. return ''
  154. return default_username
  155. signals.post_migrate.connect(create_permissions,
  156. dispatch_uid="django.contrib.auth.management.create_permissions")