[PYTHON] Comment mettre à jour les informations utilisateur sur Django RemoteUserMiddleware login

Contexte

Utilisez RemoteUserMiddleware pour faire du SSO avec Django.

Du côté de l'application Django, l'utilisateur est identifié à l'aide de la variable d'environnement REMOTE_USER définie par le module SAML (Shibboleth dans son propre environnement) en tant qu'information d'ID, mais à ce stade, les attributs autres que les informations d'ID (par exemple, l'email) sont également inclus dans la classe User du côté de l'application Django. Je veux le capturer.

Article connexe: Django + Shibboleth avec RemoteUserMiddleware (Explication de l'introduction de RemoteUserMiddleware)

environnement

--Windows Server 2016 (pas directement lié) --Apache 2.4 (pas directement lié) --Python 3.7.7 (pas directement lié)

Connaissances préalables

RemoteUserBackend recherche dans le modèle utilisateur spécifié par settings.AUTH_USER_MODEL un objet qui a la variable d'environnement REMOTE_USER comme nom d'utilisateur. Si trouvé, connectez-vous en tant que cet utilisateur. S'il n'est pas trouvé, créez un objet Utilisateur avec REMOTE_USER comme nom d'utilisateur et connectez-vous (ce paramètre peut être modifié).

Étape 1: Remplacez configure_user

À l'origine, RemoteUserBackend utilise la méthode pour manipuler l'objet User lors de la création de l'objet User du côté de l'application Django.

python:django.contrib.auth.backends


class RemoteUserBackend(ModelBackend):
...
    def configure_user(self, request, user):
        """
        Configure a user after creation and return the updated user.

        By default, return the user unmodified.
        """
        return user

Créez backends.py (n'importe quel nom) dans une application appropriée.

appname
└── appname
    ├── templates
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    ├── views.py
    ├── wsgi.py
    └── backends.py  #← Créer

Remplacez configure_user. Cette fois, j'ai décidé de saisir l'attribut ʻATR_mail` ajouté par Shibboleth à l'en-tête HTTP dans l'objet User.

backends.py


from django.contrib.auth.backends import RemoteUserBackend

class MyRemoteUserBackend(RemoteUserBackend):
    def configure_user(self, request, user: MyUser):
        user.email = request.META['ATR_mail']
        user.save() #← N'oubliez pas! !!
        return user

Pour activer cela, vous devez modifier ʻAUTHENTICATION_BACKENDS dans settings.py`.

settings.py


AUTHENTICATION_BACKENDS = (
    # 'django.contrib.auth.backends.RemoteUserBackend', #← Supprimer
    'appname.backends.MyRemoteUserBackend', #← Ajouter
    'django.contrib.auth.backends.ModelBackend',
)

Étape 2: Remplacer «authentifier»

En remplaçant simplement configure_user, configure_user ne sera exécuté que lorsque vous créerez un nouvel objet User pour REMOTE_USER, qui est nouveau dans Django, et que vous vous connecterez. ** **

L'objet utilisateur existant n'est pas mis à jour même si les informations d'attribut du côté IdP changent, mais cela semble être gênant dans de nombreux cas. Par exemple, si vous utilisez l'authentification unique dans une entreprise et que le numéro de service ou de poste change de temps en temps.

Dans ce cas, vous devez modifier ʻauthentifier. Heureusement, vous pouvez utiliser le MyRemoteUserBackend de backends.pycréé précédemment. La plupart sont des copies de la superclasseRemote User Backend ʻauthenticate.

backends.py


from django.contrib.auth.backends import RemoteUserBackend

from django.contrib.auth.models import User

import inspect
import warnings

from django.contrib.auth import get_user_model
from django.utils.deprecation import RemovedInDjango31Warning

UserModel = get_user_model()

class MyRemoteUserBackend(RemoteUserBackend):

    def authenticate(self, request, remote_user):
        """
        The username passed as ``remote_user`` is considered trusted. Return
        the ``User`` object with the given username. Create a new ``User``
        object if ``create_unknown_user`` is ``True``.

        Return None if ``create_unknown_user`` is ``False`` and a ``User``
        object with the given username is not found in the database.
        """
        if not remote_user:
            return
        user = None
        username = self.clean_username(remote_user)

        # Note that this could be accomplished in one try-except clause, but
        # instead we use get_or_create when creating unknown users since it has
        # built-in safeguards for multiple threads.
        if self.create_unknown_user:
            user, created = UserModel._default_manager.get_or_create(**{
                UserModel.USERNAME_FIELD: username
            })
            if created:                                      #← Attention
                args = (request, user)
                try:
                    inspect.getcallargs(self.configure_user, request, user)
                except TypeError:
                    args = (user,)
                    warnings.warn(
                        'Update %s.configure_user() to accept `request` as '
                        'the first argument.'
                        % self.__class__.__name__, RemovedInDjango31Warning
                    )
                user = self.configure_user(*args)            #← Attention
            else:                                            #← Ajouter
                user = self.configure_user(request, user)    #← Ajouter
        else:
            try:
                user = UserModel._default_manager.get_by_natural_key(username)
            except UserModel.DoesNotExist:
                pass
        return user if self.user_can_authenticate(user) else None

    def configure_user(self, request, user: User):
        user.email = request.META['ATR_mail']
        user.save()
        return user

Comme indiqué dans "Attention", configure_user est appelé à l'origine uniquement lorsque ʻUserModel._default_manager.get_or_create devient created == True`. Cela ne vous permet pas de mettre à jour les informations utilisateur existantes.

Par conséquent, ajoutez deux lignes de "add" pour que configure_user soit appelé même lorsque created! = True. Si vous voulez séparer l'opération pour la nouvelle création et la mise à jour de l'objet User, vous pouvez imiter configure_user et créer un nouvel ʻupdate_user` etc. pour qu'il soit appelé.

c'est tout.

Recommended Posts

Comment mettre à jour les informations utilisateur sur Django RemoteUserMiddleware login
Passer les informations de connexion à afficher dans Django
Comment mettre à jour Spyder dans Anaconda
Comment refléter CSS dans Django
Comment générer des informations supplémentaires lors de la sortie de journaux avec le module de journalisation de python
Comment supprimer des sessions expirées dans Django
Comment faire des événements envoyés par le serveur dans Django
Afficher les informations utilisateur, etc. dans le journal Django
Comment implémenter la fonctionnalité de type helper Rails dans Django
[Django] Comment résoudre les erreurs lors de l'installation de mysqlclient
Comment refléter ImageField dans Django + Docker (oreiller)
Comment créer une API Rest dans Django
Comment masquer l'entrée utilisateur dans l'interface graphique PySimple
[Django memo] Je souhaite définir à l'avance les informations de l'utilisateur connecté dans le formulaire.
Comment mettre à jour easy_install
Comment mettre à jour Spyder
mettre à jour django version 1.11.1 vers 2.2
Comment obtenir plusieurs objets de modèle au hasard dans Django
Comment accéder avec cache lors de la lecture_json avec pandas
Comment utiliser le bootstrap dans la vue de classe générique Django
Comment quitter lors de l'utilisation de Python dans Terminal (Mac)
Comment télécharger des fichiers dans la vue de classe générique Django
Comment utiliser Decorator dans Django et comment le créer
Comment référencer des fichiers statiques dans un projet Django
Comment utiliser fixture dans Django pour saisir des exemples de données associés au modèle utilisateur
Comment écrire une validation personnalisée dans Django REST Framework
Gestion des fichiers statiques lors du déploiement en production avec Django
Comment utiliser ORM / Query Builder Orator de type Laravel avec Django
Comment mettre à jour avec SQLAlchemy?
[Django] Comment donner des valeurs d'entrée à l'avance avec ModelForm
Comment résoudre la protection CSRF lors de l'utilisation d'AngularJS avec Django
Comment générer une requête à l'aide de l'opérateur IN dans Django
Comment spécifier des arguments de ligne de commande lors du débogage avec PyCharm
Que faire lorsque "En-tête HTTP_HOST non valide" apparaît dans Django
Dans Django, comment abréger la longue chaîne de caractères affichée au milieu ...
Comment développer en Python
Changer le message affiché lors de la connexion à Raspberry Pi
Comment ne pas échapper au japonais en traitant avec JSON en Python
Acquérir automatiquement le journal des opérations dans le terminal lors de la connexion à Linux
[Linux] Je souhaite connaître la date à laquelle l'utilisateur s'est connecté
Comment afficher des formules en latex lors de l'utilisation de Sympy (> = 1.4) dans Google Colaboratory
[Django] Comment lire les variables / constantes définies dans un fichier externe
Comment déployer une application Django sur heroku en seulement 5 minutes
Comment s'améliorer lorsque l'éditeur de Spyder est très lourd dans Mavericks
[Astuces] Comment étendre le modèle lors de la création de HTML avec django
[Python] Comment faire PCA avec Python
Comment gérer une session dans SQLAlchemy
[Django] Comment tester le formulaire [TDD]
Comment utiliser les classes dans Theano
Comment écrire sobrement avec des pandas
Comment collecter des images en Python
Erreur liée à memcached dans django
Comment utiliser SQLite en Python
Comment utiliser le module de journalisation de Python
Comment ajouter sudo lors de l'exécution du débogage
Comment convertir 0,5 en 1056964608 en un seul coup
Comment démarrer avec Django
Comment tuer des processus en vrac