We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Password change and reset endpoints throw 500 instead of returning error messages
## Description
* Try updating a password with a common password such as `password`.
* The /password_reset and /password_change endpoints throw a 500
* They should return a 400 with formatted error messages

Related: 
* https://github.com/centerofci/mathesar/issues/1673

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mathesar/api/exceptions/mixins.py`
Content:
```
1 from rest_framework.serializers import ListSerializer, Serializer
2 from rest_framework.utils.serializer_helpers import ReturnList
3 from rest_framework_friendly_errors.mixins import FriendlyErrorMessagesMixin
4 from django.core.exceptions import ValidationError as DjangoValidationError
5 from rest_framework.exceptions import ValidationError as RestValidationError
6 from mathesar.api.exceptions.generic_exceptions.base_exceptions import ErrorBody
7 
8 
9 class MathesarErrorMessageMixin(FriendlyErrorMessagesMixin):
10     def is_pretty(self, error):
11         return isinstance(error, dict) and tuple(error.keys()) == ErrorBody._fields
12 
13     def build_pretty_errors(self, errors, serializer=None):
14 
15         """
16         This method build on top of `build_pretty_errors` method of the superclass
17         It provides the following additional features
18         1. Avoids processing prettified exceptions
19         2. Add field to the pretty exception body if raised by field validation method
20         """
21         pretty = []
22         if self.is_pretty(errors):
23             # DRF serializers supports error any of the following format, error string, list of error strings or {'field': [error strings]}
24             # Since our exception is an object instead of a string, the object properties are mistaken to be fields of a serializer,
25             # and it gets converted into {'field': [error strings]} by DRF
26             # We need to convert it to dictionary of list of object and return it instead of passing it down the line
27             scalar_errors = dict(map(lambda item: (item[0], item[1][0] if type(item[1]) == list else item[1]), errors.items()))
28             return [scalar_errors]
29         for error_type in errors:
30             error = errors[error_type]
31             if error_type == 'non_field_errors':
32                 if self.is_pretty(error):
33                     pretty.append(error)
34                 else:
35                     pretty.extend(self.get_non_field_error_entries(errors[error_type]))
36             else:
37                 field = self.get_serializer_fields(self.initial_data).fields[error_type]
38                 if isinstance(field, Serializer) and type(errors[error_type]) == dict:
39                     field.initial_data = self.initial_data[error_type]
40                     child_errors = field.build_pretty_errors(errors[error_type])
41                     pretty += child_errors
42                     continue
43                 if isinstance(field, ListSerializer) and type(errors[error_type]) == list:
44                     pretty_child_errors = []
45                     for index, child_error in enumerate(errors[error_type]):
46                         child_field = field.child
47                         initial_data = self.initial_data.get(error_type, None)
48                         if initial_data is not None:
49                             child_field.initial_data = self.initial_data[error_type][index]
50                         else:
51                             child_field.initial_data = None
52                         if isinstance(child_error, str):
53                             pretty_child_errors.append(self.get_field_error_entry(child_error, field))
54                         else:
55                             child_errors = child_field.build_pretty_errors(child_error)
56                             pretty_child_errors.extend(child_errors)
57                     pretty.extend(pretty_child_errors)
58                     continue
59                 if self.is_pretty(error):
60                     if 'field' not in error or error['field'] is None or str(error['field']) == 'None':
61                         error['field'] = error_type
62                     pretty.append(error)
63                 else:
64                     pretty.extend(self.get_field_error_entries(errors[error_type], field))
65         if pretty:
66             return pretty
67         return []
68 
69     def get_serializer_fields(self, data):
70         return self.fields
71 
72     def _run_validator(self, validator, field, message):
73         """
74         This method build on top of `_run_validator` method of the superclass
75         It provides the following additional features
76         1. Includes serializer if `required_context` is True similar to the behaviour of drf
77         """
78         try:
79             args = []
80             if getattr(validator, 'requires_context', False):
81                 args.append(field)
82             validator(self.initial_data[field.field_name], *args)
83         except (DjangoValidationError, RestValidationError) as err:
84             err_message = err.detail[0] if hasattr(err, 'detail') else err.message
85             return err_message == message
86 
87     @property
88     def errors(self):
89         """
90         This method build on top of `errors` property of the superclass to return a list instead of a dictionary
91         """
92         ugly_errors = super(FriendlyErrorMessagesMixin, self).errors
93         pretty_errors = self.build_pretty_errors(ugly_errors)
94         return ReturnList(pretty_errors, serializer=self)
95 
96     @property
97     def field_map(self):
98         """
99         This method build on top of `field_map` property of the superclass
100         It provides the following additional features
101         1. Adds `ListSerializer` to `relation` field list
102         """
103         parent_field_map = super(FriendlyErrorMessagesMixin, self).field_map
104         # Add missing `ListSerializer to existing relation list`
105         parent_field_map['relation'].append('ListSerializer')
106         parent_field_map['relation'].append('PermittedPkRelatedField')
107         parent_field_map['relation'].append('PermittedSlugRelatedField')
108         return parent_field_map
109 
110     def get_field_kwargs(self, field, field_data):
111         """
112         This method build on top of `get_field_kwargs` method of the superclass
113         It provides the following fixes
114         1. Fixes file type length value to use name of the file instead of the size of the file,
115          matching the default behaviour of drf
116         """
117         field_type = field.__class__.__name__
118         kwargs = {
119             'data_type': type(field_data).__name__
120         }
121         if field_type in self.field_map['boolean']:
122             kwargs.update({'input': field_data})
123         elif field_type in self.field_map['string']:
124             kwargs.update(
125                 {
126                     'max_length': getattr(field, 'max_length', None),
127                     'min_length': getattr(field, 'min_length', None),
128                     'value': field_data
129                 }
130             )
131         elif field_type in self.field_map['numeric']:
132 
133             kwargs.update(
134                 {
135                     'min_value': field.min_value,
136                     'max_value': field.max_value,
137                     'decimal_places': getattr(field, 'decimal_places', None),
138                     'max_decimal_places': getattr(field, 'decimal_places', None),
139                     'max_digits': getattr(field, 'max_digits', None)
140                 }
141             )
142             max_digits = kwargs['max_digits']
143             decimal_places = kwargs['decimal_places']
144             if max_digits is not None and decimal_places is not None:
145                 whole_digits = max_digits - decimal_places
146                 kwargs.update({'max_whole_digits': whole_digits})
147         elif field_type in self.field_map['date'].keys():
148             kwargs.update({'format': self.field_map['date'][field_type]})
149         elif field_type in self.field_map['choice']:
150             kwargs.update(
151                 {
152                     'input': field_data,
153                     'input_type': type(field_data).__name__
154                 }
155             )
156         elif field_type in self.field_map['file']:
157             # Parent method calculates the length of the file instead of the filename,
158             # we are changing it to calculate length of the file name
159             file_field = field.parent.data.get(field.source, None)
160             file_name_length = len(file_field.name) if file_field else 0
161             kwargs.update(
162                 {
163                     'max_length': field.max_length,
164                     'length': file_name_length
165                 }
166             )
167         elif field_type in self.field_map['composite']:
168             kwargs.update(
169                 {
170                     'input_type': type(field_data).__name__,
171                     'max_length': getattr(field, 'max_length', None),
172                     'min_length': getattr(field, 'min_length', None)
173                 }
174             )
175         elif field_type in self.field_map['relation']:
176             kwargs.update(
177                 {
178                     'pk_value': field_data,
179                     'data_type': type(field_data).__name__,
180                     'input_type': type(field_data).__name__,
181                     'slug_name': getattr(field, 'slug_field', None),
182                     'value': field_data
183                 }
184             )
185         else:
186             kwargs.update({'max_length': getattr(field, 'max_length', None)})
187         return kwargs
188
```

Path: `mathesar/api/ui/serializers/users.py`
Content:
```
1 from django.contrib.auth.password_validation import validate_password
2 from rest_access_policy import FieldAccessMixin, PermittedPkRelatedField
3 from rest_framework import serializers
4 
5 from mathesar.api.db.permissions.database import DatabaseAccessPolicy
6 from mathesar.api.db.permissions.schema import SchemaAccessPolicy
7 from mathesar.api.exceptions.mixins import MathesarErrorMessageMixin
8 from mathesar.api.exceptions.validation_exceptions.exceptions import IncorrectOldPassword
9 from mathesar.api.ui.permissions.users import UserAccessPolicy
10 from mathesar.models.base import Database, Schema
11 from mathesar.models.users import User, DatabaseRole, SchemaRole
12 
13 
14 class NestedDatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
15     class Meta:
16         model = DatabaseRole
17         fields = ['id', 'database', 'role']
18 
19 
20 class NestedSchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
21     class Meta:
22         model = SchemaRole
23         fields = ['id', 'schema', 'role']
24 
25 
26 class UserSerializer(MathesarErrorMessageMixin, FieldAccessMixin, serializers.ModelSerializer):
27     database_roles = NestedDatabaseRoleSerializer(many=True, required=False)
28     schema_roles = NestedSchemaRoleSerializer(many=True, required=False)
29     access_policy = UserAccessPolicy
30 
31     class Meta:
32         model = User
33         fields = [
34             'id',
35             'full_name',
36             'short_name',
37             'username',
38             'password',
39             'email',
40             'is_superuser',
41             'database_roles',
42             'schema_roles',
43         ]
44         extra_kwargs = {
45             'password': {'write_only': True},
46             'database_roles': {'read_only': True},
47             'schema_roles': {'read_only': True}
48         }
49 
50     def get_fields(self):
51         fields = super().get_fields()
52         request = self.context.get("request", None)
53         if not hasattr(request, 'parser_context'):
54             return fields
55         kwargs = request.parser_context.get('kwargs')
56         if kwargs:
57             user_pk = kwargs.get('pk')
58             if user_pk:
59                 if request.user.id == int(user_pk) or not request.user.is_superuser:
60                     fields["is_superuser"].read_only = True
61         return fields
62 
63     def create(self, validated_data):
64         password = validated_data.pop('password')
65         user = User(**validated_data)
66         user.password_change_needed = True
67         user.set_password(password)
68         user.save()
69         return user
70 
71 
72 class ChangePasswordSerializer(serializers.Serializer):
73     password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
74     old_password = serializers.CharField(write_only=True, required=True)
75 
76     def validate_old_password(self, value):
77         user = self.context['request'].user
78         if user.check_password(value) is True:
79             return value
80         raise IncorrectOldPassword(field='old_password')
81 
82     def update(self, instance, validated_data):
83         instance.set_password(validated_data['password'])
84         instance.save()
85         return instance
86 
87 
88 class PasswordResetSerializer(MathesarErrorMessageMixin, serializers.Serializer):
89     password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
90 
91 
92 class DatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
93     class Meta:
94         model = DatabaseRole
95         fields = ['id', 'user', 'database', 'role']
96 
97     # Restrict the list of databases to which the user has access to create a database role
98     # Refer https://rsinger86.github.io/drf-access-policy/policy_reuse/ for the usage of `PermittedPkRelatedField`
99     database = PermittedPkRelatedField(
100         access_policy=DatabaseAccessPolicy,
101         queryset=Database.current_objects.all()
102     )
103 
104 
105 class SchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):
106     class Meta:
107         model = SchemaRole
108         fields = ['id', 'user', 'schema', 'role']
109 
110     schema = PermittedPkRelatedField(
111         access_policy=SchemaAccessPolicy,
112         queryset=Schema.current_objects.all()
113     )
114
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mathesar/api/exceptions/mixins.py b/mathesar/api/exceptions/mixins.py
--- a/mathesar/api/exceptions/mixins.py
+++ b/mathesar/api/exceptions/mixins.py
@@ -81,7 +81,12 @@
                 args.append(field)
             validator(self.initial_data[field.field_name], *args)
         except (DjangoValidationError, RestValidationError) as err:
-            err_message = err.detail[0] if hasattr(err, 'detail') else err.message
+            if hasattr(err, 'detail'):
+                err_message = err.detail[0]
+            elif hasattr(err, 'message'):
+                err_message = err.message
+            elif hasattr(err, 'messages'):
+                err_message = err.messages[0]
             return err_message == message
 
     @property
diff --git a/mathesar/api/ui/serializers/users.py b/mathesar/api/ui/serializers/users.py
--- a/mathesar/api/ui/serializers/users.py
+++ b/mathesar/api/ui/serializers/users.py
@@ -69,7 +69,7 @@
         return user
 
 
-class ChangePasswordSerializer(serializers.Serializer):
+class ChangePasswordSerializer(MathesarErrorMessageMixin, serializers.Serializer):
     password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
     old_password = serializers.CharField(write_only=True, required=True)

{"golden_diff": "diff --git a/mathesar/api/exceptions/mixins.py b/mathesar/api/exceptions/mixins.py\n--- a/mathesar/api/exceptions/mixins.py\n+++ b/mathesar/api/exceptions/mixins.py\n@@ -81,7 +81,12 @@\n                 args.append(field)\n             validator(self.initial_data[field.field_name], *args)\n         except (DjangoValidationError, RestValidationError) as err:\n-            err_message = err.detail[0] if hasattr(err, 'detail') else err.message\n+            if hasattr(err, 'detail'):\n+                err_message = err.detail[0]\n+            elif hasattr(err, 'message'):\n+                err_message = err.message\n+            elif hasattr(err, 'messages'):\n+                err_message = err.messages[0]\n             return err_message == message\n \n     @property\ndiff --git a/mathesar/api/ui/serializers/users.py b/mathesar/api/ui/serializers/users.py\n--- a/mathesar/api/ui/serializers/users.py\n+++ b/mathesar/api/ui/serializers/users.py\n@@ -69,7 +69,7 @@\n         return user\n \n \n-class ChangePasswordSerializer(serializers.Serializer):\n+class ChangePasswordSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n     password = serializers.CharField(write_only=True, required=True, validators=[validate_password])\n     old_password = serializers.CharField(write_only=True, required=True)\n", "issue": "Password change and reset endpoints throw 500 instead of returning error messages\n## Description\r\n* Try updating a password with a common password such as `password`.\r\n* The /password_reset and /password_change endpoints throw a 500\r\n* They should return a 400 with formatted error messages\r\n\r\nRelated: \r\n* https://github.com/centerofci/mathesar/issues/1673\n", "before_files": [{"content": "from rest_framework.serializers import ListSerializer, Serializer\nfrom rest_framework.utils.serializer_helpers import ReturnList\nfrom rest_framework_friendly_errors.mixins import FriendlyErrorMessagesMixin\nfrom django.core.exceptions import ValidationError as DjangoValidationError\nfrom rest_framework.exceptions import ValidationError as RestValidationError\nfrom mathesar.api.exceptions.generic_exceptions.base_exceptions import ErrorBody\n\n\nclass MathesarErrorMessageMixin(FriendlyErrorMessagesMixin):\n    def is_pretty(self, error):\n        return isinstance(error, dict) and tuple(error.keys()) == ErrorBody._fields\n\n    def build_pretty_errors(self, errors, serializer=None):\n\n        \"\"\"\n        This method build on top of `build_pretty_errors` method of the superclass\n        It provides the following additional features\n        1. Avoids processing prettified exceptions\n        2. Add field to the pretty exception body if raised by field validation method\n        \"\"\"\n        pretty = []\n        if self.is_pretty(errors):\n            # DRF serializers supports error any of the following format, error string, list of error strings or {'field': [error strings]}\n            # Since our exception is an object instead of a string, the object properties are mistaken to be fields of a serializer,\n            # and it gets converted into {'field': [error strings]} by DRF\n            # We need to convert it to dictionary of list of object and return it instead of passing it down the line\n            scalar_errors = dict(map(lambda item: (item[0], item[1][0] if type(item[1]) == list else item[1]), errors.items()))\n            return [scalar_errors]\n        for error_type in errors:\n            error = errors[error_type]\n            if error_type == 'non_field_errors':\n                if self.is_pretty(error):\n                    pretty.append(error)\n                else:\n                    pretty.extend(self.get_non_field_error_entries(errors[error_type]))\n            else:\n                field = self.get_serializer_fields(self.initial_data).fields[error_type]\n                if isinstance(field, Serializer) and type(errors[error_type]) == dict:\n                    field.initial_data = self.initial_data[error_type]\n                    child_errors = field.build_pretty_errors(errors[error_type])\n                    pretty += child_errors\n                    continue\n                if isinstance(field, ListSerializer) and type(errors[error_type]) == list:\n                    pretty_child_errors = []\n                    for index, child_error in enumerate(errors[error_type]):\n                        child_field = field.child\n                        initial_data = self.initial_data.get(error_type, None)\n                        if initial_data is not None:\n                            child_field.initial_data = self.initial_data[error_type][index]\n                        else:\n                            child_field.initial_data = None\n                        if isinstance(child_error, str):\n                            pretty_child_errors.append(self.get_field_error_entry(child_error, field))\n                        else:\n                            child_errors = child_field.build_pretty_errors(child_error)\n                            pretty_child_errors.extend(child_errors)\n                    pretty.extend(pretty_child_errors)\n                    continue\n                if self.is_pretty(error):\n                    if 'field' not in error or error['field'] is None or str(error['field']) == 'None':\n                        error['field'] = error_type\n                    pretty.append(error)\n                else:\n                    pretty.extend(self.get_field_error_entries(errors[error_type], field))\n        if pretty:\n            return pretty\n        return []\n\n    def get_serializer_fields(self, data):\n        return self.fields\n\n    def _run_validator(self, validator, field, message):\n        \"\"\"\n        This method build on top of `_run_validator` method of the superclass\n        It provides the following additional features\n        1. Includes serializer if `required_context` is True similar to the behaviour of drf\n        \"\"\"\n        try:\n            args = []\n            if getattr(validator, 'requires_context', False):\n                args.append(field)\n            validator(self.initial_data[field.field_name], *args)\n        except (DjangoValidationError, RestValidationError) as err:\n            err_message = err.detail[0] if hasattr(err, 'detail') else err.message\n            return err_message == message\n\n    @property\n    def errors(self):\n        \"\"\"\n        This method build on top of `errors` property of the superclass to return a list instead of a dictionary\n        \"\"\"\n        ugly_errors = super(FriendlyErrorMessagesMixin, self).errors\n        pretty_errors = self.build_pretty_errors(ugly_errors)\n        return ReturnList(pretty_errors, serializer=self)\n\n    @property\n    def field_map(self):\n        \"\"\"\n        This method build on top of `field_map` property of the superclass\n        It provides the following additional features\n        1. Adds `ListSerializer` to `relation` field list\n        \"\"\"\n        parent_field_map = super(FriendlyErrorMessagesMixin, self).field_map\n        # Add missing `ListSerializer to existing relation list`\n        parent_field_map['relation'].append('ListSerializer')\n        parent_field_map['relation'].append('PermittedPkRelatedField')\n        parent_field_map['relation'].append('PermittedSlugRelatedField')\n        return parent_field_map\n\n    def get_field_kwargs(self, field, field_data):\n        \"\"\"\n        This method build on top of `get_field_kwargs` method of the superclass\n        It provides the following fixes\n        1. Fixes file type length value to use name of the file instead of the size of the file,\n         matching the default behaviour of drf\n        \"\"\"\n        field_type = field.__class__.__name__\n        kwargs = {\n            'data_type': type(field_data).__name__\n        }\n        if field_type in self.field_map['boolean']:\n            kwargs.update({'input': field_data})\n        elif field_type in self.field_map['string']:\n            kwargs.update(\n                {\n                    'max_length': getattr(field, 'max_length', None),\n                    'min_length': getattr(field, 'min_length', None),\n                    'value': field_data\n                }\n            )\n        elif field_type in self.field_map['numeric']:\n\n            kwargs.update(\n                {\n                    'min_value': field.min_value,\n                    'max_value': field.max_value,\n                    'decimal_places': getattr(field, 'decimal_places', None),\n                    'max_decimal_places': getattr(field, 'decimal_places', None),\n                    'max_digits': getattr(field, 'max_digits', None)\n                }\n            )\n            max_digits = kwargs['max_digits']\n            decimal_places = kwargs['decimal_places']\n            if max_digits is not None and decimal_places is not None:\n                whole_digits = max_digits - decimal_places\n                kwargs.update({'max_whole_digits': whole_digits})\n        elif field_type in self.field_map['date'].keys():\n            kwargs.update({'format': self.field_map['date'][field_type]})\n        elif field_type in self.field_map['choice']:\n            kwargs.update(\n                {\n                    'input': field_data,\n                    'input_type': type(field_data).__name__\n                }\n            )\n        elif field_type in self.field_map['file']:\n            # Parent method calculates the length of the file instead of the filename,\n            # we are changing it to calculate length of the file name\n            file_field = field.parent.data.get(field.source, None)\n            file_name_length = len(file_field.name) if file_field else 0\n            kwargs.update(\n                {\n                    'max_length': field.max_length,\n                    'length': file_name_length\n                }\n            )\n        elif field_type in self.field_map['composite']:\n            kwargs.update(\n                {\n                    'input_type': type(field_data).__name__,\n                    'max_length': getattr(field, 'max_length', None),\n                    'min_length': getattr(field, 'min_length', None)\n                }\n            )\n        elif field_type in self.field_map['relation']:\n            kwargs.update(\n                {\n                    'pk_value': field_data,\n                    'data_type': type(field_data).__name__,\n                    'input_type': type(field_data).__name__,\n                    'slug_name': getattr(field, 'slug_field', None),\n                    'value': field_data\n                }\n            )\n        else:\n            kwargs.update({'max_length': getattr(field, 'max_length', None)})\n        return kwargs\n", "path": "mathesar/api/exceptions/mixins.py"}, {"content": "from django.contrib.auth.password_validation import validate_password\nfrom rest_access_policy import FieldAccessMixin, PermittedPkRelatedField\nfrom rest_framework import serializers\n\nfrom mathesar.api.db.permissions.database import DatabaseAccessPolicy\nfrom mathesar.api.db.permissions.schema import SchemaAccessPolicy\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\nfrom mathesar.api.exceptions.validation_exceptions.exceptions import IncorrectOldPassword\nfrom mathesar.api.ui.permissions.users import UserAccessPolicy\nfrom mathesar.models.base import Database, Schema\nfrom mathesar.models.users import User, DatabaseRole, SchemaRole\n\n\nclass NestedDatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = DatabaseRole\n        fields = ['id', 'database', 'role']\n\n\nclass NestedSchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = SchemaRole\n        fields = ['id', 'schema', 'role']\n\n\nclass UserSerializer(MathesarErrorMessageMixin, FieldAccessMixin, serializers.ModelSerializer):\n    database_roles = NestedDatabaseRoleSerializer(many=True, required=False)\n    schema_roles = NestedSchemaRoleSerializer(many=True, required=False)\n    access_policy = UserAccessPolicy\n\n    class Meta:\n        model = User\n        fields = [\n            'id',\n            'full_name',\n            'short_name',\n            'username',\n            'password',\n            'email',\n            'is_superuser',\n            'database_roles',\n            'schema_roles',\n        ]\n        extra_kwargs = {\n            'password': {'write_only': True},\n            'database_roles': {'read_only': True},\n            'schema_roles': {'read_only': True}\n        }\n\n    def get_fields(self):\n        fields = super().get_fields()\n        request = self.context.get(\"request\", None)\n        if not hasattr(request, 'parser_context'):\n            return fields\n        kwargs = request.parser_context.get('kwargs')\n        if kwargs:\n            user_pk = kwargs.get('pk')\n            if user_pk:\n                if request.user.id == int(user_pk) or not request.user.is_superuser:\n                    fields[\"is_superuser\"].read_only = True\n        return fields\n\n    def create(self, validated_data):\n        password = validated_data.pop('password')\n        user = User(**validated_data)\n        user.password_change_needed = True\n        user.set_password(password)\n        user.save()\n        return user\n\n\nclass ChangePasswordSerializer(serializers.Serializer):\n    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])\n    old_password = serializers.CharField(write_only=True, required=True)\n\n    def validate_old_password(self, value):\n        user = self.context['request'].user\n        if user.check_password(value) is True:\n            return value\n        raise IncorrectOldPassword(field='old_password')\n\n    def update(self, instance, validated_data):\n        instance.set_password(validated_data['password'])\n        instance.save()\n        return instance\n\n\nclass PasswordResetSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])\n\n\nclass DatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = DatabaseRole\n        fields = ['id', 'user', 'database', 'role']\n\n    # Restrict the list of databases to which the user has access to create a database role\n    # Refer https://rsinger86.github.io/drf-access-policy/policy_reuse/ for the usage of `PermittedPkRelatedField`\n    database = PermittedPkRelatedField(\n        access_policy=DatabaseAccessPolicy,\n        queryset=Database.current_objects.all()\n    )\n\n\nclass SchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = SchemaRole\n        fields = ['id', 'user', 'schema', 'role']\n\n    schema = PermittedPkRelatedField(\n        access_policy=SchemaAccessPolicy,\n        queryset=Schema.current_objects.all()\n    )\n", "path": "mathesar/api/ui/serializers/users.py"}], "after_files": [{"content": "from rest_framework.serializers import ListSerializer, Serializer\nfrom rest_framework.utils.serializer_helpers import ReturnList\nfrom rest_framework_friendly_errors.mixins import FriendlyErrorMessagesMixin\nfrom django.core.exceptions import ValidationError as DjangoValidationError\nfrom rest_framework.exceptions import ValidationError as RestValidationError\nfrom mathesar.api.exceptions.generic_exceptions.base_exceptions import ErrorBody\n\n\nclass MathesarErrorMessageMixin(FriendlyErrorMessagesMixin):\n    def is_pretty(self, error):\n        return isinstance(error, dict) and tuple(error.keys()) == ErrorBody._fields\n\n    def build_pretty_errors(self, errors, serializer=None):\n\n        \"\"\"\n        This method build on top of `build_pretty_errors` method of the superclass\n        It provides the following additional features\n        1. Avoids processing prettified exceptions\n        2. Add field to the pretty exception body if raised by field validation method\n        \"\"\"\n        pretty = []\n        if self.is_pretty(errors):\n            # DRF serializers supports error any of the following format, error string, list of error strings or {'field': [error strings]}\n            # Since our exception is an object instead of a string, the object properties are mistaken to be fields of a serializer,\n            # and it gets converted into {'field': [error strings]} by DRF\n            # We need to convert it to dictionary of list of object and return it instead of passing it down the line\n            scalar_errors = dict(map(lambda item: (item[0], item[1][0] if type(item[1]) == list else item[1]), errors.items()))\n            return [scalar_errors]\n        for error_type in errors:\n            error = errors[error_type]\n            if error_type == 'non_field_errors':\n                if self.is_pretty(error):\n                    pretty.append(error)\n                else:\n                    pretty.extend(self.get_non_field_error_entries(errors[error_type]))\n            else:\n                field = self.get_serializer_fields(self.initial_data).fields[error_type]\n                if isinstance(field, Serializer) and type(errors[error_type]) == dict:\n                    field.initial_data = self.initial_data[error_type]\n                    child_errors = field.build_pretty_errors(errors[error_type])\n                    pretty += child_errors\n                    continue\n                if isinstance(field, ListSerializer) and type(errors[error_type]) == list:\n                    pretty_child_errors = []\n                    for index, child_error in enumerate(errors[error_type]):\n                        child_field = field.child\n                        initial_data = self.initial_data.get(error_type, None)\n                        if initial_data is not None:\n                            child_field.initial_data = self.initial_data[error_type][index]\n                        else:\n                            child_field.initial_data = None\n                        if isinstance(child_error, str):\n                            pretty_child_errors.append(self.get_field_error_entry(child_error, field))\n                        else:\n                            child_errors = child_field.build_pretty_errors(child_error)\n                            pretty_child_errors.extend(child_errors)\n                    pretty.extend(pretty_child_errors)\n                    continue\n                if self.is_pretty(error):\n                    if 'field' not in error or error['field'] is None or str(error['field']) == 'None':\n                        error['field'] = error_type\n                    pretty.append(error)\n                else:\n                    pretty.extend(self.get_field_error_entries(errors[error_type], field))\n        if pretty:\n            return pretty\n        return []\n\n    def get_serializer_fields(self, data):\n        return self.fields\n\n    def _run_validator(self, validator, field, message):\n        \"\"\"\n        This method build on top of `_run_validator` method of the superclass\n        It provides the following additional features\n        1. Includes serializer if `required_context` is True similar to the behaviour of drf\n        \"\"\"\n        try:\n            args = []\n            if getattr(validator, 'requires_context', False):\n                args.append(field)\n            validator(self.initial_data[field.field_name], *args)\n        except (DjangoValidationError, RestValidationError) as err:\n            if hasattr(err, 'detail'):\n                err_message = err.detail[0]\n            elif hasattr(err, 'message'):\n                err_message = err.message\n            elif hasattr(err, 'messages'):\n                err_message = err.messages[0]\n            return err_message == message\n\n    @property\n    def errors(self):\n        \"\"\"\n        This method build on top of `errors` property of the superclass to return a list instead of a dictionary\n        \"\"\"\n        ugly_errors = super(FriendlyErrorMessagesMixin, self).errors\n        pretty_errors = self.build_pretty_errors(ugly_errors)\n        return ReturnList(pretty_errors, serializer=self)\n\n    @property\n    def field_map(self):\n        \"\"\"\n        This method build on top of `field_map` property of the superclass\n        It provides the following additional features\n        1. Adds `ListSerializer` to `relation` field list\n        \"\"\"\n        parent_field_map = super(FriendlyErrorMessagesMixin, self).field_map\n        # Add missing `ListSerializer to existing relation list`\n        parent_field_map['relation'].append('ListSerializer')\n        parent_field_map['relation'].append('PermittedPkRelatedField')\n        parent_field_map['relation'].append('PermittedSlugRelatedField')\n        return parent_field_map\n\n    def get_field_kwargs(self, field, field_data):\n        \"\"\"\n        This method build on top of `get_field_kwargs` method of the superclass\n        It provides the following fixes\n        1. Fixes file type length value to use name of the file instead of the size of the file,\n         matching the default behaviour of drf\n        \"\"\"\n        field_type = field.__class__.__name__\n        kwargs = {\n            'data_type': type(field_data).__name__\n        }\n        if field_type in self.field_map['boolean']:\n            kwargs.update({'input': field_data})\n        elif field_type in self.field_map['string']:\n            kwargs.update(\n                {\n                    'max_length': getattr(field, 'max_length', None),\n                    'min_length': getattr(field, 'min_length', None),\n                    'value': field_data\n                }\n            )\n        elif field_type in self.field_map['numeric']:\n\n            kwargs.update(\n                {\n                    'min_value': field.min_value,\n                    'max_value': field.max_value,\n                    'decimal_places': getattr(field, 'decimal_places', None),\n                    'max_decimal_places': getattr(field, 'decimal_places', None),\n                    'max_digits': getattr(field, 'max_digits', None)\n                }\n            )\n            max_digits = kwargs['max_digits']\n            decimal_places = kwargs['decimal_places']\n            if max_digits is not None and decimal_places is not None:\n                whole_digits = max_digits - decimal_places\n                kwargs.update({'max_whole_digits': whole_digits})\n        elif field_type in self.field_map['date'].keys():\n            kwargs.update({'format': self.field_map['date'][field_type]})\n        elif field_type in self.field_map['choice']:\n            kwargs.update(\n                {\n                    'input': field_data,\n                    'input_type': type(field_data).__name__\n                }\n            )\n        elif field_type in self.field_map['file']:\n            # Parent method calculates the length of the file instead of the filename,\n            # we are changing it to calculate length of the file name\n            file_field = field.parent.data.get(field.source, None)\n            file_name_length = len(file_field.name) if file_field else 0\n            kwargs.update(\n                {\n                    'max_length': field.max_length,\n                    'length': file_name_length\n                }\n            )\n        elif field_type in self.field_map['composite']:\n            kwargs.update(\n                {\n                    'input_type': type(field_data).__name__,\n                    'max_length': getattr(field, 'max_length', None),\n                    'min_length': getattr(field, 'min_length', None)\n                }\n            )\n        elif field_type in self.field_map['relation']:\n            kwargs.update(\n                {\n                    'pk_value': field_data,\n                    'data_type': type(field_data).__name__,\n                    'input_type': type(field_data).__name__,\n                    'slug_name': getattr(field, 'slug_field', None),\n                    'value': field_data\n                }\n            )\n        else:\n            kwargs.update({'max_length': getattr(field, 'max_length', None)})\n        return kwargs\n", "path": "mathesar/api/exceptions/mixins.py"}, {"content": "from django.contrib.auth.password_validation import validate_password\nfrom rest_access_policy import FieldAccessMixin, PermittedPkRelatedField\nfrom rest_framework import serializers\n\nfrom mathesar.api.db.permissions.database import DatabaseAccessPolicy\nfrom mathesar.api.db.permissions.schema import SchemaAccessPolicy\nfrom mathesar.api.exceptions.mixins import MathesarErrorMessageMixin\nfrom mathesar.api.exceptions.validation_exceptions.exceptions import IncorrectOldPassword\nfrom mathesar.api.ui.permissions.users import UserAccessPolicy\nfrom mathesar.models.base import Database, Schema\nfrom mathesar.models.users import User, DatabaseRole, SchemaRole\n\n\nclass NestedDatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = DatabaseRole\n        fields = ['id', 'database', 'role']\n\n\nclass NestedSchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = SchemaRole\n        fields = ['id', 'schema', 'role']\n\n\nclass UserSerializer(MathesarErrorMessageMixin, FieldAccessMixin, serializers.ModelSerializer):\n    database_roles = NestedDatabaseRoleSerializer(many=True, required=False)\n    schema_roles = NestedSchemaRoleSerializer(many=True, required=False)\n    access_policy = UserAccessPolicy\n\n    class Meta:\n        model = User\n        fields = [\n            'id',\n            'full_name',\n            'short_name',\n            'username',\n            'password',\n            'email',\n            'is_superuser',\n            'database_roles',\n            'schema_roles',\n        ]\n        extra_kwargs = {\n            'password': {'write_only': True},\n            'database_roles': {'read_only': True},\n            'schema_roles': {'read_only': True}\n        }\n\n    def get_fields(self):\n        fields = super().get_fields()\n        request = self.context.get(\"request\", None)\n        if not hasattr(request, 'parser_context'):\n            return fields\n        kwargs = request.parser_context.get('kwargs')\n        if kwargs:\n            user_pk = kwargs.get('pk')\n            if user_pk:\n                if request.user.id == int(user_pk) or not request.user.is_superuser:\n                    fields[\"is_superuser\"].read_only = True\n        return fields\n\n    def create(self, validated_data):\n        password = validated_data.pop('password')\n        user = User(**validated_data)\n        user.password_change_needed = True\n        user.set_password(password)\n        user.save()\n        return user\n\n\nclass ChangePasswordSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])\n    old_password = serializers.CharField(write_only=True, required=True)\n\n    def validate_old_password(self, value):\n        user = self.context['request'].user\n        if user.check_password(value) is True:\n            return value\n        raise IncorrectOldPassword(field='old_password')\n\n    def update(self, instance, validated_data):\n        instance.set_password(validated_data['password'])\n        instance.save()\n        return instance\n\n\nclass PasswordResetSerializer(MathesarErrorMessageMixin, serializers.Serializer):\n    password = serializers.CharField(write_only=True, required=True, validators=[validate_password])\n\n\nclass DatabaseRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = DatabaseRole\n        fields = ['id', 'user', 'database', 'role']\n\n    # Restrict the list of databases to which the user has access to create a database role\n    # Refer https://rsinger86.github.io/drf-access-policy/policy_reuse/ for the usage of `PermittedPkRelatedField`\n    database = PermittedPkRelatedField(\n        access_policy=DatabaseAccessPolicy,\n        queryset=Database.current_objects.all()\n    )\n\n\nclass SchemaRoleSerializer(MathesarErrorMessageMixin, serializers.ModelSerializer):\n    class Meta:\n        model = SchemaRole\n        fields = ['id', 'user', 'schema', 'role']\n\n    schema = PermittedPkRelatedField(\n        access_policy=SchemaAccessPolicy,\n        queryset=Schema.current_objects.all()\n    )\n", "path": "mathesar/api/ui/serializers/users.py"}]}