Project

General

Profile

Bug #24401

crash de /api/user/ sur un profil contenant un champ date

Added by Frédéric Péters about 1 year ago. Updated 5 months ago.

Status:
En cours
Priority:
Normal
Category:
-
Target version:
-
Start date:
10 Jun 2018
Due date:
% Done:

0%

Patch proposed:
No
Planning:
No

Description

Environment:

Request Method: GET
Request URL: https://authentic.fred.local.0d.be/api/user/

Django Version: 1.8.18
Python Version: 2.7.13
Installed Applications:
''
Installed Middleware:
''

Traceback:
File "/home/fred/src/eo/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/fred/src/eo/venv/local/lib/python2.7/site-packages/django/views/decorators/vary.py" in inner_func
  21.             response = func(*args, **kwargs)
File "/home/fred/src/eo/venv/local/lib/python2.7/site-packages/django/views/decorators/cache.py" in _cache_controlled
  43.             response = viewfunc(request, *args, **kw)
File "/home/fred/src/eo/authentic/src/authentic2/decorators.py" in f
  297.         json_str = json_dumps(result)
File "/usr/lib/python2.7/json/__init__.py" in dumps
  244.         return _default_encoder.encode(obj)
File "/usr/lib/python2.7/json/encoder.py" in encode
  207.         chunks = self.iterencode(o, _one_shot=True)
File "/usr/lib/python2.7/json/encoder.py" in iterencode
  270.         return _iterencode(o, 0)
File "/usr/lib/python2.7/json/encoder.py" in default
  184.         raise TypeError(repr(o) + " is not JSON serializable")

Exception Type: TypeError at /api/user/
Exception Value: datetime.date(2018, 3, 20) is not JSON serializable

0001-user-api-fix-attribute-value-serialization-24401.patch View (915 Bytes) Paul Marillonnet, 02 Aug 2018 02:08 PM

History

#1 Updated by Paul Marillonnet about 1 year ago

  • Assignee set to Paul Marillonnet

Je prends.

#2 Updated by Paul Marillonnet about 1 year ago

Frédéric, si tu as quelques infos en plus, pour reproduire le bogue par exemple, je suis preneur stp.

Edit: par exemple, sais-tu à quoi correspond ce champ date ? Le BaseUserSerializer des vues d'API d'authentic prends en charge User.date_joined et User.last_login, mais qui sont tous deux des datetime.datetime, pas des datetime.date

#3 Updated by Frédéric Péters about 1 year ago

Avoir un champ date dans le profil. Avoir une valeur dedans. Aller avec son navigateur à l'adresse /api/user/.

#4 Updated by Paul Marillonnet about 1 year ago

Frédéric Péters a écrit :

Avoir un champ date dans le profil.

Pas compris. Ce champ date ne correspond à aucun attribut dans le modèle utilisateur d'authentic.

Tu comprendras peut-être ce que je ne comprends pas si je copie-colle ma tentative de reproduction du bogue :

In [1]: import datetime

In [2]: from django.contrib.auth import get_user_model

In [3]: User = get_user_model()

In [4]: User.objects.all()
Out[4]: [<User: u' (e3b055)'>, <User: u'admin (844b60)'>, <User: u'paul (ee1dc9)'>, <User: u'root (a25fcb)'>]

In [5]: root = User.objects.last()

In [6]: root.date = datetime.date(2018,3,20)

In [7]: root.save()

In [8]:                                                                                        
Do you really want to exit ([y]/n)?     
[2018-06-14 Thu 15:06:51] - - - WARNING py.warnings.adapt_datetime_with_timezone_support: /home/paul/eodevel/virtualenvs/main2/local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py:57: RuntimeWarning: SQLite received a naive datetime (2018-06-14 15:06:51.994598) while time zone support is active.
  RuntimeWarning)

(main2) paul@eosandbox:~/eodevel/authentic$ ./authentic2-ctl runserver 192.168.56.101:8080
Performing system checks...

System check identified no issues (1 silenced).
June 14, 2018 - 15:06:55
Django version 1.8.19, using settings 'authentic2.settings'
Starting development server at http://192.168.56.101:8080/
Quit the server with CONTROL-C.
[14/Jun/2018 15:06:56] "GET /api/user/ HTTP/1.1" 200 243

#5 Updated by Benjamin Dauvergne about 1 year ago

Les attributs ne sont pas stockés dans le modèle User mais dans le modèle AttributeValue, il y a un descripteur qui produit des accésseurs via user.attributes.<nom_de_l'attribut>.

#7 Updated by Frédéric Péters about 1 year ago

Si tu utilises Publik, tu peux activer le champ "date de naissance" depuis la page de définition du profil dans Hobo. Si c'est un authentic pur, tu peux aller dans /admin/ créer un objet de type Attribute.

#8 Updated by Paul Marillonnet about 1 year ago

Frédéric Péters a écrit :

Si tu utilises Publik, tu peux activer le champ "date de naissance" depuis la page de définition du profil dans Hobo. Si c'est un authentic pur, tu peux aller dans /admin/ créer un objet de type Attribute.

Ok, je vais faire comme ça, merci.

Pour info, j'avais essayé, en vain, de créer un nouvel attribut de type date depuis cette page de définition du profil dans Hobo. On dirait que les nouveaux attributs de profil usager ne peuvent être que de type string. Est-ce que ça pourrait faire l'objet d'un ticket d'évolution ?
J'ai l'impression qu'on ne peut pas supprimer de tels attributs une fois ceux-ci créés, on peut seulement les désactiver, je me plante ? Est-ce que ça pourrait être une autre demande d'évolution ?

#9 Updated by Paul Marillonnet about 1 year ago

Bogue reproduit, je m'y colle, merci pour votre aide Frédéric et Benjamin.

#10 Updated by Frédéric Péters about 1 year ago

Pour info, j'avais essayé, en vain, de créer un nouvel attribut de type date depuis cette page de définition du profil dans Hobo. On dirait que les nouveaux attributs de profil usager ne peuvent être que de type string. Est-ce que ça pourrait faire l'objet d'un ticket d'évolution ?

Been there, done that, #23306. Tu devrais mettre à jour.

#11 Updated by Paul Marillonnet 12 months ago

Mis à jour, merci.
#23306 n'apporte pas la définition d'attributs de type date, je vais donc passer par l'interface /admin/ pour reproduire le problème.

#12 Updated by Paul Marillonnet 11 months ago

#13 Updated by Frédéric Péters 11 months ago

Là tu zappes tout à fait le code de désérialisation des attributs.

Ce qu'il faut à mon sens c'est plutôt modifier le décorateur json, qu'il utilise django.core.serializers.json.DjangoJSONEncoder, qui sait comment gérer les dates.

#14 Updated by Benjamin Dauvergne 11 months ago

Il faudrait soir utiliser directement le serializer DRF dans User.to_json soit ajouter une méthode to_json() à Attribute qui utilise le paramétrage rest_framework_field_class des types d'attribut (voir authentic2/attribute_kinds.py).

#15 Updated by Paul Marillonnet 11 months ago

Je loupe visiblement quelque chose.

Pour moi c'est justement l'appel à to_python qui zappe le code précédemment exécuté pour la sérialisation des attributs.

models.Attribute.set_value place la valeur sérialisée dans Attribute.content, et c'est normal qu'on accède à ce champ lors de la méthode custom_user.models.User.to_json, non ?

Pourquoi faire appel à de la désérialisation (to_python) à ce moment là ?
to_python va executer models.AttributeValue.attribute.get_kind()['deserialize'], qui est censé être l'opération complémentaire de la sérialisation précédemment effectuée.
C'est le serpent qui se mord la queue, non? :)

#16 Updated by Paul Marillonnet 10 months ago

Frédéric Péters a écrit :

Là tu zappes tout à fait le code de désérialisation des attributs.

Ce qu'il faut à mon sens c'est plutôt modifier le décorateur json, qu'il utilise django.core.serializers.json.DjangoJSONEncoder, qui sait comment gérer les dates.

Ok, en relisant le code je comprends mon erreur.
Les attributs sont sérialisés à des fins n'ayant rien à voir avec l'API.
Il faut les désérialiser dans un premier temps, puis un nouveau coup de moulinette (décorateur json) pour que le tout sorte en JSON.

#17 Updated by Frédéric Péters 5 months ago

  • Status changed from Solution proposée to En cours
  • Patch proposed changed from Yes to No

Also available in: Atom PDF