class MailboxSerializer(serializers.ModelSerializer): """Base mailbox serializer.""" full_address = lib_fields.DRFEmailFieldUTF8() quota = serializers.CharField(required=False) class Meta: model = models.Mailbox fields = ( "pk", "full_address", "use_domain_quota", "quota", "message_limit" ) def validate_full_address(self, value): """Lower case address.""" return value.lower() def validate_quota(self, value): """Convert quota to MB.""" return web_utils.size2integer(value, output_unit="MB") def validate(self, data): """Check if quota is required.""" method = self.context["request"].method if not data.get("use_domain_quota", False): if "quota" not in data and method != "PATCH": raise serializers.ValidationError({ "quota": _("This field is required") }) return data
class AccountSerializer(v1_serializers.AccountSerializer): """Add support for user resources.""" aliases = serializers.ListField(child=lib_fields.DRFEmailFieldUTF8(), source="mailbox.alias_addresses") class Meta(v1_serializers.AccountSerializer.Meta): fields = (v1_serializers.AccountSerializer.Meta.fields + ("aliases", )) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not param_tools.get_global_parameter("enable_admin_limits", app="limits"): return self.fields["resources"] = serializers.SerializerMethodField() def get_resources(self, account) -> List[AccountResourceSerializer]: if account.role == 'SimpleUsers': return [] resources = [] for limit in account.userobjectlimit_set.all(): tpl = limits_constants.DEFAULT_USER_LIMITS[limit.name] if "required_role" in tpl: if account.role != tpl["required_role"]: continue resources.append(limit) return AccountResourceSerializer(resources, many=True).data
class MailboxSerializer(serializers.ModelSerializer): """Base mailbox serializer.""" full_address = lib_fields.DRFEmailFieldUTF8() class Meta: model = models.Mailbox fields = ("full_address", "use_domain_quota", "quota", )
class MailboxSerializer(serializers.ModelSerializer): """Mailbox serializer.""" pk = serializers.IntegerField() full_address = lib_fields.DRFEmailFieldUTF8() class Meta: model = admin_models.Mailbox fields = ("pk", "full_address") read_only_fields = ( "pk", "full_address", )
class MailboxSerializer(serializers.ModelSerializer): """Base mailbox serializer.""" full_address = lib_fields.DRFEmailFieldUTF8() class Meta: model = models.Mailbox fields = ( "pk", "full_address", "use_domain_quota", "quota", ) def validate_full_address(self, value): """Lower case address.""" return value.lower()
class MailboxSerializer(serializers.ModelSerializer): """Base mailbox serializer.""" full_address = lib_fields.DRFEmailFieldUTF8() quota = serializers.CharField() class Meta: model = models.Mailbox fields = ("pk", "full_address", "use_domain_quota", "quota", ) def validate_full_address(self, value): """Lower case address.""" return value.lower() def validate_quota(self, value): """Convert quota to MB.""" return web_utils.size2integer(value, output_unit="MB")
class ResetPasswordSerializer(serializers.Serializer): """Serializer by the reset password endpoint.""" email = lib_fields.DRFEmailFieldUTF8()
class WritableAccountSerializer(v1_serializers.WritableAccountSerializer): """Add support for aliases and sender addresses.""" aliases = serializers.ListField(child=lib_fields.DRFEmailFieldUTF8(), required=False) mailbox = MailboxSerializer(required=False) class Meta(v1_serializers.WritableAccountSerializer.Meta): fields = tuple( field for field in v1_serializers.WritableAccountSerializer.Meta.fields if field != "random_password") + ("aliases", ) def validate_aliases(self, value): """Check if required domains are locals and user can access them.""" aliases = [] for alias in value: localpart, domain = models.validate_alias_address( alias, self.context["request"].user) aliases.append({"localpart": localpart, "domain": domain}) return aliases def validate(self, data): """Check constraints.""" master_user = data.get("master_user", False) role = data.get("role") if master_user and role != "SuperAdmins": raise serializers.ValidationError( {"master_user": _("Not allowed for this role.")}) if role == "SimpleUsers": username = data.get("username") if username: try: validators.UTF8EmailValidator()(username) except ValidationError as err: raise ValidationError({"username": err.message}) mailbox = data.get("mailbox") if mailbox is None: if not self.instance: data["mailbox"] = {"use_domain_quota": True} if data.get("password") or not self.partial: password = data.get("password") if password: try: password_validation.validate_password( data["password"], self.instance) except ValidationError as exc: raise serializers.ValidationError( {"password": exc.messages[0]}) elif not self.instance: raise serializers.ValidationError( {"password": _("This field is required.")}) aliases = data.get("aliases") if aliases and "mailbox" not in data: raise serializers.ValidationError( {"aliases": _("A mailbox is required to create aliases.")}) domain_names = data.get("domains") if not domain_names: return data domains = [] for name in domain_names: domain = models.Domain.objects.filter(name=name).first() if domain: domains.append(domain) continue raise serializers.ValidationError( {"domains": _("Local domain {} does not exist").format(name)}) data["domains"] = domains return data def create(self, validated_data): """Create account, mailbox and aliases.""" creator = self.context["request"].user mailbox_data = validated_data.pop("mailbox", None) role = validated_data.pop("role") domains = validated_data.pop("domains", []) aliases = validated_data.pop("aliases", None) user = core_models.User(**validated_data) password = validated_data.pop("password") user.set_password(password) if "language" not in validated_data: user.language = settings.LANGUAGE_CODE user.save(creator=creator) if mailbox_data: mailbox_data["full_address"] = user.username self._create_mailbox(creator, user, mailbox_data) user.role = role self.set_permissions(user, domains) if aliases: for alias in aliases: models.Alias.objects.create(creator=creator, domain=alias["domain"], address="{}@{}".format( alias["localpart"], alias["domain"]), recipients=[user.username]) return user
class CoreGlobalParametersSerializer(serializers.Serializer): """A serializer for global parameters.""" # General settings authentication_type = serializers.ChoiceField( choices=[("local", ugettext_lazy("Local")), ("ldap", "LDAP")], default="local" ) password_scheme = serializers.ChoiceField( choices=[("sha512crypt", "sha512crypt"), ("sha256crypt", "sha256crypt"), ("blfcrypt", "bcrypt"), ("md5crypt", ugettext_lazy("md5crypt (weak)")), ("sha256", ugettext_lazy("sha256 (weak)")), ("md5", ugettext_lazy("md5 (weak)")), ("crypt", ugettext_lazy("crypt (weak)")), ("plain", ugettext_lazy("plain (weak)"))], default="sha512crypt" ) rounds_number = serializers.IntegerField(default=70000) update_scheme = serializers.BooleanField(default=True) default_password = serializers.CharField(default="password") random_password_length = serializers.IntegerField(min_value=8, default=8) update_password_url = serializers.URLField(required=False, allow_blank=True) password_recovery_msg = serializers.CharField( required=False, allow_blank=True) sms_password_recovery = serializers.BooleanField(default=False) sms_provider = serializers.ChoiceField( choices=constants.SMS_BACKENDS, required=False) # LDAP settings ldap_server_address = serializers.CharField(default="localhost") ldap_server_port = serializers.IntegerField(default=389) ldap_enable_secondary_server = serializers.BooleanField(default=False) ldap_secondary_server_address = serializers.CharField(required=False) ldap_secondary_server_port = serializers.IntegerField( default=389, required=False) ldap_secured = serializers.ChoiceField( choices=constants.LDAP_SECURE_MODES, default="none" ) ldap_is_active_directory = serializers.BooleanField(default=False) ldap_admin_groups = serializers.CharField( default="", required=False, allow_blank=True) ldap_group_type = serializers.ChoiceField( default="posixgroup", choices=constants.LDAP_GROUP_TYPES ) ldap_groups_search_base = serializers.CharField( default="", required=False, allow_blank=True) ldap_password_attribute = serializers.CharField(default="userPassword") # LDAP auth settings ldap_auth_method = serializers.ChoiceField( choices=constants.LDAP_AUTH_METHODS, default="searchbind", ) ldap_bind_dn = serializers.CharField( default="", required=False, allow_blank=True) ldap_bind_password = serializers.CharField( default="", required=False, allow_blank=True) ldap_search_base = serializers.CharField( default="", required=False, allow_blank=True) ldap_search_filter = serializers.CharField( default="(mail=%(user)s)", required=False, allow_blank=True) ldap_user_dn_template = serializers.CharField( default="", required=False, allow_blank=True) # LDAP sync settings ldap_sync_bind_dn = serializers.CharField(required=False, allow_blank=True) ldap_sync_bind_password = serializers.CharField( required=False, allow_blank=True) ldap_enable_sync = serializers.BooleanField(default=False) ldap_sync_delete_remote_account = serializers.BooleanField(default=False) ldap_sync_account_dn_template = serializers.CharField( required=False, allow_blank=True) ldap_enable_import = serializers.BooleanField(default=False) ldap_import_search_base = serializers.CharField( required=False, allow_blank=True) ldap_import_search_filter = serializers.CharField( default="(cn=*)", required=False ) ldap_import_username_attr = serializers.CharField(default="cn") ldap_dovecot_sync = serializers.BooleanField(default=False) ldap_dovecot_conf_file = serializers.CharField( default="/etc/dovecot/dovecot-modoboa.conf", required=False ) # Dashboard settings rss_feed_url = serializers.URLField(allow_blank=True) hide_features_widget = serializers.BooleanField(default=False) # Notification settings sender_address = lib_fields.DRFEmailFieldUTF8( default="*****@*****.**") # API settings enable_api_communication = serializers.BooleanField(default=True) check_new_versions = serializers.BooleanField(default=True) send_new_versions_email = serializers.BooleanField(default=False) new_versions_email_rcpt = lib_fields.DRFEmailFieldUTF8(required=False) send_statistics = serializers.BooleanField(default=True) # Misc settings inactive_account_threshold = serializers.IntegerField(default=30) top_notifications_check_interval = serializers.IntegerField(default=30) log_maximum_age = serializers.IntegerField(default=365) items_per_page = serializers.IntegerField(default=30) default_top_redirection = serializers.ChoiceField( default="user", choices=[""]) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) sms_backend_fields = sms_backends.get_all_backend_serializer_settings() for field, definition in sms_backend_fields.items(): self.fields[field] = definition["type"]( **definition["attrs"]) def validate_ldap_user_dn_template(self, value): try: value % {"user": "******"} except (KeyError, ValueError): raise serializers.ValidationError(_("Invalid syntax")) return value def validate_ldap_sync_account_dn_template(self, value): try: value % {"user": "******"} except (KeyError, ValueError): raise serializers.ValidationError(_("Invalid syntax")) return value def validate_ldap_search_filter(self, value): try: value % {"user": "******"} except (KeyError, ValueError, TypeError): raise serializers.ValidationError(_("Invalid syntax")) return value def validate_rounds_number(self, value): if value < 1000 or value > 999999999: raise serializers.ValidationError(_("Invalid rounds number")) return value def validate_default_password(self, value): """Check password complexity.""" password_validation.validate_password(value) return value def validate(self, data): """Custom validation method Depending on 'ldap_auth_method' value, we check for different required parameters. """ errors = {} if data["sms_password_recovery"]: provider = data.get("sms_provider") if provider: sms_settings = sms_backends.get_backend_settings(provider) if sms_settings: for name in sms_settings.keys(): if not data.get(name): errors[name] = _("This field is required") else: errors["sms_provider"] = _("This field is required") if data["authentication_type"] == "ldap": if data["ldap_auth_method"] == "searchbind": required_fields = ["ldap_search_base", "ldap_search_filter"] else: required_fields = ["ldap_user_dn_template"] for f in required_fields: if data.get(f, "") == "": errors[f] = _("This field is required") if len(errors): raise serializers.ValidationError(errors) return data
class WritableAccountSerializer(v1_serializers.WritableAccountSerializer): """Add support for aliases and sender addresses.""" aliases = serializers.ListField(child=lib_fields.DRFEmailFieldUTF8(), required=False) mailbox = MailboxSerializer(required=False) class Meta(v1_serializers.WritableAccountSerializer.Meta): fields = tuple( field for field in v1_serializers.WritableAccountSerializer.Meta.fields if field != "random_password") + ("aliases", ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if not param_tools.get_global_parameter("enable_admin_limits", app="limits"): return self.fields["resources"] = WritableResourceSerializer(many=True, required=False) def validate_aliases(self, value): """Check if required domains are locals and user can access them.""" aliases = [] for alias in value: localpart, domain = models.validate_alias_address( alias, self.context["request"].user) aliases.append({"localpart": localpart, "domain": domain}) return aliases def validate(self, data): """Check constraints.""" master_user = data.get("master_user", False) role = data.get("role") if master_user and role != "SuperAdmins": raise serializers.ValidationError( {"master_user": _("Not allowed for this role.")}) if role == "SimpleUsers": username = data.get("username") if username: try: validators.UTF8EmailValidator()(username) except ValidationError as err: raise ValidationError({"username": err.message}) mailbox = data.get("mailbox") if mailbox is None: if not self.instance: data["mailbox"] = {"use_domain_quota": True} if "mailbox" in data and "username" in data: self.address, domain_name = email_utils.split_mailbox( data["username"]) self.domain = get_object_or_404(models.Domain, name=domain_name) creator = self.context["request"].user if not creator.can_access(self.domain): raise serializers.ValidationError( {"mailbox": _("Permission denied.")}) if not self.instance: try: core_signals.can_create_object.send(sender=self.__class__, context=creator, klass=models.Mailbox) core_signals.can_create_object.send( sender=self.__class__, context=self.domain, object_type="mailboxes") except lib_exceptions.ModoboaException as inst: raise serializers.ValidationError({"mailbox": str(inst)}) if data.get("password") or not self.partial: password = data.get("password") if password: try: password_validation.validate_password( data["password"], self.instance) except ValidationError as exc: raise serializers.ValidationError( {"password": exc.messages[0]}) elif not self.instance: raise serializers.ValidationError( {"password": _("This field is required.")}) aliases = data.get("aliases") if aliases and "mailbox" not in data: raise serializers.ValidationError( {"aliases": _("A mailbox is required to create aliases.")}) domain_names = data.get("domains") if not domain_names: return data domains = [] for name in domain_names: domain = models.Domain.objects.filter(name=name).first() if domain: domains.append(domain) continue raise serializers.ValidationError( {"domains": _("Local domain {} does not exist").format(name)}) data["domains"] = domains return data def create(self, validated_data): """Create account, mailbox and aliases.""" creator = self.context["request"].user mailbox_data = validated_data.pop("mailbox", None) role = validated_data.pop("role") domains = validated_data.pop("domains", []) aliases = validated_data.pop("aliases", None) user = core_models.User(**validated_data) password = validated_data.pop("password") user.set_password(password) if "language" not in validated_data: user.language = settings.LANGUAGE_CODE user.save(creator=creator) if mailbox_data: mailbox_data["full_address"] = user.username self._create_mailbox(creator, user, mailbox_data) user.role = role self.set_permissions(user, domains) if aliases: for alias in aliases: models.Alias.objects.create(creator=creator, domain=alias["domain"], address="{}@{}".format( alias["localpart"], alias["domain"]), recipients=[user.username]) return user def update(self, instance, validated_data): """Update account and associated objects.""" mailbox_data = validated_data.pop("mailbox", None) password = validated_data.pop("password", None) domains = validated_data.pop("domains", []) for key, value in validated_data.items(): setattr(instance, key, value) if password: instance.set_password(password) if mailbox_data: creator = self.context["request"].user if hasattr(instance, "mailbox"): if "username" in validated_data: mailbox_data["email"] = validated_data["username"] instance.email = validated_data["username"] instance.mailbox.update_from_dict(creator, mailbox_data) elif "username" in validated_data: mailbox_data["full_address"] = validated_data["username"] instance.email = validated_data["username"] self._create_mailbox(creator, instance, mailbox_data) instance.save() resources = validated_data.get("resources") if resources: for resource in resources: instance.userobjectlimit_set.filter( name=resource["name"]).update( max_value=resource["max_value"]) self.set_permissions(instance, domains) return instance