from rest_framework import serializers from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.core.exceptions import ValidationError as DjangoValidationError from eox_tenant.models import TenantConfig User = get_user_model() class CreateTenantSerializer(serializers.Serializer): """Serializer for tenant creation request.""" tenant_name = serializers.RegexField( regex=r'^[a-z0-9-]+$', max_length=63, help_text="Tenant name (lowercase alphanumeric and hyphens only)" ) platform_name = serializers.CharField(max_length=255, required=False) theme_name = serializers.CharField(max_length=255, required=False, default='indigo') org_filter = serializers.ListField( child=serializers.CharField(), required=False, help_text="List of course organizations for this tenant" ) # EDNX settings (all optional with sensible defaults) ednx_tenant_restrict_users = serializers.BooleanField( required=False, default=True, help_text="Restrict users to their assigned tenant (EDNX_TENANT_RESTRICT_USERS)" ) ednx_tenant_user_filter_enabled = serializers.BooleanField( required=False, default=True, help_text="Enable user filtering by tenant (EDNX_TENANT_USER_FILTER_ENABLED)" ) ednx_use_signal = serializers.BooleanField( required=False, default=True, help_text="Use signals for tenant context (EDNX_USE_SIGNAL)" ) ednx_account_registration_sources = serializers.ListField( child=serializers.CharField(), required=False, help_text="Allowed account registration sources (auto-generated from tenant domain if not provided)" ) def validate_tenant_name(self, value): """Ensure tenant name doesn't use reserved words.""" reserved = ['local', 'studio', 'apps', 'meilisearch', 'www', 'api', 'admin'] if value in reserved: raise serializers.ValidationError(f"'{value}' is a reserved name") return value class TenantResponseSerializer(serializers.Serializer): """Serializer for tenant creation response.""" status = serializers.CharField() tenant_id = serializers.IntegerField() tenant_name = serializers.CharField() lms_url = serializers.CharField() authn_url = serializers.CharField() learner_dashboard_url = serializers.CharField() class TenantAdminCreateSerializer(serializers.Serializer): """Serializer for tenant admin creation request.""" tenant_name = serializers.CharField( max_length=63, help_text="Tenant name (must exist)" ) username = serializers.CharField( max_length=150, help_text="Username for the admin user" ) email = serializers.EmailField( help_text="Email address for the admin user" ) password = serializers.CharField( write_only=True, help_text="Password for the admin user" ) org_name = serializers.CharField( max_length=255, help_text="Organization name for role assignment" ) def validate_tenant_name(self, value): """Validate that tenant exists.""" external_key = f"{value}.local.openedx.io" if not TenantConfig.objects.filter(external_key=external_key).exists(): raise serializers.ValidationError("Tenant does not exist") return value def validate_username(self, value): """Validate username is unique.""" if User.objects.filter(username=value).exists(): raise serializers.ValidationError("Username already exists") return value def validate_email(self, value): """Validate email is unique.""" if User.objects.filter(email=value).exists(): raise serializers.ValidationError("Email already exists") return value def validate_password(self, value): """Validate password meets Django security requirements.""" try: validate_password(value) except DjangoValidationError as e: raise serializers.ValidationError(list(e.messages)) return value