Projet

Général

Profil

Bug #24401

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

Ajouté par Frédéric Péters il y a 9 mois. Mis à jour il y a 2 mois.

Statut:
En cours
Priorité:
Normal
Assigné à:
Catégorie:
-
Version cible:
-
Début:
10 juin 2018
Echéance:
% réalisé:

0%

Patch proposed:
Non

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 Voir (915 octets) Paul Marillonnet, 02 août 2018 14:08

Historique

#1 Mis à jour par Paul Marillonnet il y a 9 mois

  • Assigné à mis à Paul Marillonnet

Je prends.

#2 Mis à jour par Paul Marillonnet il y a 9 mois

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 Mis à jour par Frédéric Péters il y a 9 mois

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

#4 Mis à jour par Paul Marillonnet il y a 9 mois

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 Mis à jour par Benjamin Dauvergne il y a 9 mois

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>.

#6 Mis à jour par Benjamin Dauvergne il y a 9 mois

#7 Mis à jour par Frédéric Péters il y a 9 mois

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 Mis à jour par Paul Marillonnet il y a 9 mois

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 Mis à jour par Paul Marillonnet il y a 9 mois

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

#10 Mis à jour par Frédéric Péters il y a 9 mois

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 Mis à jour par Paul Marillonnet il y a 9 mois

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 Mis à jour par Paul Marillonnet il y a 8 mois

#13 Mis à jour par Frédéric Péters il y a 8 mois

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 Mis à jour par Benjamin Dauvergne il y a 8 mois

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 Mis à jour par Paul Marillonnet il y a 8 mois

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 Mis à jour par Paul Marillonnet il y a 7 mois

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 Mis à jour par Frédéric Péters il y a 2 mois

  • Statut changé de Solution proposée à En cours
  • Patch proposed changé de Oui à Non

Formats disponibles : Atom PDF