"""
This module provides a Django management command to create or update a superuser ("Smarter admin").
account, user profile, and a corresponding API key for the platform.
Classes
=======
Command
Implements the logic for the ``manage.py create_smarter_admin`` command.
Command-line Arguments
=====================
-u, --username : str, optional
The username for the new superuser (defaults to value from settings).
-e, --email : str, optional
The email address for the new superuser (defaults to username@domain).
-p, --password : str, optional
The password for the new superuser. If not specified, a random password is generated.
Functionality
=============
- Ensures there is a platform admin account in the system, fully populated.
- Creates or updates User and UserProfile for the superuser.
- Creates a default AccountContact if absent.
- Creates and displays a new API key (auth token) for the superuser if one does not exist.
- Most account and contact attributes are populated from configuration settings.
Usage Example
=============
python manage.py create_smarter_admin --username=admin --email=admin@example.com --password=s3cret
"""
import secrets
import string
from smarter.apps.account.models import Account, AccountContact, User, UserProfile
from smarter.common.conf import smarter_settings
from smarter.common.const import (
SMARTER_ACCOUNT_NUMBER,
SMARTER_ADMIN_USERNAME,
SMARTER_CUSTOMER_SUPPORT_EMAIL,
SMARTER_CUSTOMER_SUPPORT_PHONE,
)
from smarter.lib.django.management.base import SmarterCommand
from smarter.lib.drf.models import SmarterAuthToken
# pylint: disable=E1101
[docs]
class Command(SmarterCommand):
"""Create a new Smarter superuser."""
[docs]
def add_arguments(self, parser):
"""Add arguments to the command."""
parser.add_argument("-u", "--username", type=str, help="The username for the new superuser")
parser.add_argument("-e", "--email", type=str, help="The email address for the new superuser")
parser.add_argument("-p", "--password", type=str, help="The password for the new superuser")
[docs]
def handle(self, *args, **options):
"""Create the superuser account."""
self.handle_begin()
username = options["username"] or SMARTER_ADMIN_USERNAME
email = options["email"] or f"{username}@{smarter_settings.root_api_domain}"
password = options["password"]
account, created = Account.objects.get_or_create(
account_number=SMARTER_ACCOUNT_NUMBER,
)
account.name = f"{smarter_settings.platform_name} Admin Account"
account.description = f"automatically-generated {smarter_settings.platform_name} Admin account"
account.company_name = smarter_settings.branding_corporate_name
account.is_default_account = True
account.phone_number = smarter_settings.branding_support_phone_number
account.address1 = smarter_settings.branding_address1
account.address2 = smarter_settings.branding_address2
account.city = smarter_settings.branding_city
account.state = smarter_settings.branding_state
account.postal_code = smarter_settings.branding_postal_code
account.country = smarter_settings.branding_country
account.timezone = smarter_settings.branding_timezone
account.currency = smarter_settings.branding_currency
account.save()
if created:
self.handle_completed_success(msg=f"Created account: {account.account_number} {account.company_name}")
user, created = User.objects.get_or_create(username=username)
user.email = email
user.is_superuser = True
user.is_staff = True
user.is_active = True
user.save()
if created:
if not password:
password_length = 16
alphabet = string.ascii_letters + string.digits + string.punctuation
password = "".join(secrets.choice(alphabet) for _ in range(password_length))
user.set_password(password)
user.save()
self.handle_completed_success(msg=f"Created superuser {username} {email} has been created.")
else:
self.handle_completed_success(msg=f"User {username} updated.")
user_profile, created = UserProfile.objects.get_or_create(user=user, account=account)
if created:
self.handle_completed_success(
msg=f"Created user profile for {user_profile.user.username} {user_profile.user.email}, account {user_profile.account}"
)
else:
self.handle_completed_success(
msg=f"User profile already exists for {user_profile.user}, account {user_profile.account}"
)
UserProfile.get_cached_object(invalidate=True, pk=user_profile.pk) # prime the cache
account_contact, created = AccountContact.objects.get_or_create(
account=account,
is_primary=True,
)
if created:
account_contact.first_name = "Smarter"
account_contact.last_name = "Admin"
account_contact.email = SMARTER_CUSTOMER_SUPPORT_EMAIL
account_contact.phone = SMARTER_CUSTOMER_SUPPORT_PHONE
account_contact.save()
self.handle_completed_success(
msg=f"Created account contact for {account_contact.first_name} {account_contact.last_name}, account {account_contact.account.account_number} {account_contact.account.company_name}"
)
# ensure that the Smarter admin user has at least one auth token (api key)
smarterauthtoken = SmarterAuthToken.get_cached_objects(user=user)
if not smarterauthtoken.exists():
_, token_key = SmarterAuthToken.objects.create(
user_profile=user_profile, name="smarter-admin-key", user=user, description="created by manage.py"
) # type: ignore[assignment]
self.handle_completed_success(msg=f"created API key: {token_key}")
return
self.handle_completed_success()