createsuperuser.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. """
  2. Misago-native rehash of Django's createsuperuser command that
  3. works with double authentication fields on user model
  4. """
  5. import sys
  6. from getpass import getpass
  7. from django.contrib.auth import get_user_model
  8. from django.contrib.auth.password_validation import validate_password
  9. from django.core.exceptions import ValidationError
  10. from django.core.management.base import BaseCommand
  11. from django.db import DEFAULT_DB_ALIAS, IntegrityError
  12. from django.utils.encoding import force_str
  13. from misago.cache.versions import get_cache_versions
  14. from misago.conf.dynamicsettings import DynamicSettings
  15. from misago.users.setupnewuser import setup_new_user
  16. from misago.users.validators import validate_email, validate_username
  17. User = get_user_model()
  18. class NotRunningInTTYException(Exception):
  19. pass
  20. class Command(BaseCommand):
  21. help = "Used to create a superuser."
  22. def add_arguments(self, parser):
  23. parser.add_argument(
  24. "--username",
  25. dest="username",
  26. default=None,
  27. help="Specifies the username for the superuser.",
  28. )
  29. parser.add_argument(
  30. "--email",
  31. dest="email",
  32. default=None,
  33. help="Specifies the e-mail for the superuser.",
  34. )
  35. parser.add_argument(
  36. "--password",
  37. dest="password",
  38. default=None,
  39. help="Specifies the password for the superuser.",
  40. )
  41. parser.add_argument(
  42. "--noinput",
  43. "--no-input",
  44. action="store_false",
  45. dest="interactive",
  46. default=True,
  47. help=(
  48. "Tells Misago to NOT prompt the user for input "
  49. "of any kind. You must use --username with "
  50. "--noinput, along with an option for any other "
  51. "required field. Superusers created with "
  52. "--noinput will not be able to log in until "
  53. "they're given a valid password."
  54. ),
  55. )
  56. parser.add_argument(
  57. "--database",
  58. action="store",
  59. dest="database",
  60. default=DEFAULT_DB_ALIAS,
  61. help=('Specifies the database to use. Default is "default".'),
  62. )
  63. def execute(self, *args, **options):
  64. self.stdin = options.get("stdin", sys.stdin) # Used for testing
  65. return super().execute(*args, **options)
  66. def handle(self, *args, **options):
  67. username = options.get("username")
  68. email = options.get("email")
  69. password = options.get("password")
  70. interactive = options.get("interactive")
  71. verbosity = int(options.get("verbosity", 1))
  72. cache_versions = get_cache_versions()
  73. settings = DynamicSettings(cache_versions)
  74. # Validate initial inputs
  75. if username is not None:
  76. try:
  77. username = username.strip()
  78. validate_username(settings, username)
  79. except ValidationError as e:
  80. self.stderr.write("\n".join(e.messages))
  81. username = None
  82. if email is not None:
  83. try:
  84. email = email.strip()
  85. validate_email(email)
  86. except ValidationError as e:
  87. self.stderr.write("\n".join(e.messages))
  88. email = None
  89. if password is not None:
  90. password = password.strip()
  91. if password == "":
  92. self.stderr.write("Error: Blank passwords aren't allowed.")
  93. if not interactive:
  94. if username and email and password:
  95. # Call User manager's create_superuser using our wrapper
  96. self.create_superuser(username, email, password, settings, verbosity)
  97. else:
  98. try:
  99. if hasattr(self.stdin, "isatty") and not self.stdin.isatty():
  100. raise NotRunningInTTYException("Not running in a TTY")
  101. # Prompt for username/password, and any other required fields.
  102. # Enclose this whole thing in a try/except to trap for a
  103. # keyboard interrupt and exit gracefully.
  104. while not username:
  105. try:
  106. message = force_str("Enter displayed username: ")
  107. raw_value = input(message).strip()
  108. validate_username(raw_value)
  109. username = raw_value
  110. except ValidationError as e:
  111. self.stderr.write("\n".join(e.messages))
  112. while not email:
  113. try:
  114. raw_value = input("Enter e-mail address: ").strip()
  115. validate_email(raw_value)
  116. email = raw_value
  117. except ValidationError as e:
  118. self.stderr.write("\n".join(e.messages))
  119. while not password:
  120. raw_value = getpass("Enter password: ")
  121. password_repeat = getpass("Repeat password:")
  122. if raw_value != password_repeat:
  123. self.stderr.write("Error: Your passwords didn't match.")
  124. # Don't validate passwords that don't match.
  125. continue
  126. if raw_value.strip() == "":
  127. self.stderr.write("Error: Blank passwords aren't allowed.")
  128. # Don't validate blank passwords.
  129. continue
  130. try:
  131. validate_password(
  132. raw_value, user=User(username=username, email=email)
  133. )
  134. except ValidationError as e:
  135. self.stderr.write("\n".join(e.messages))
  136. response = input(
  137. "Bypass password validation and create user anyway? [y/N]: "
  138. )
  139. if response.lower() != "y":
  140. continue
  141. password = raw_value
  142. # Call User manager's create_superuser using our wrapper
  143. self.create_superuser(username, email, password, settings, verbosity)
  144. except KeyboardInterrupt:
  145. self.stderr.write("\nOperation cancelled.")
  146. sys.exit(1)
  147. except NotRunningInTTYException:
  148. self.stdout.write(
  149. "Superuser creation skipped due to not running in a TTY. "
  150. "You can run `manage.py createsuperuser` in your project "
  151. "to create one manually."
  152. )
  153. def create_superuser(self, username, email, password, settings, verbosity):
  154. try:
  155. user = User.objects.create_superuser(username, email, password)
  156. setup_new_user(settings, user)
  157. if verbosity >= 1:
  158. message = "Superuser #%s has been created successfully."
  159. self.stdout.write(message % user.pk)
  160. except ValidationError as e:
  161. self.stderr.write(e.messages[0])
  162. except IntegrityError as e:
  163. self.stderr.write(e.messages[0])