Skip to content

Trim username's spaces when user authenticates via CAS.#500

Merged
ptitloup merged 1 commit intoEsupPortail:devfrom
ZephOne:cas-sso-strip-spaces-around-username
May 28, 2021
Merged

Trim username's spaces when user authenticates via CAS.#500
ptitloup merged 1 commit intoEsupPortail:devfrom
ZephOne:cas-sso-strip-spaces-around-username

Conversation

@ZephOne
Copy link
Contributor

@ZephOne ZephOne commented May 27, 2021

This pull request assumes that there is no case needing username with spaces when CAS authentication is used. So this behavior is not configurable. However if you feel it should be, I can make it configurable.

@ptitloup
Copy link
Contributor

Merci !

@ZephOne ZephOne marked this pull request as draft May 28, 2021 06:59
def populateUser(tree):
username_element = tree.find(
'.//{http://www.yale.edu/tp/cas}%s' % AUTH_CAS_USER_SEARCH)
username_element.text = username_element.text.strip()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZephOne peut-on modifier username plutôt que username_element ?
ex:

username = username_element.text.strip()

Copy link
Contributor Author

@ZephOne ZephOne May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Si l'on fait ça, à la ligne 97 :

    user, user_created = User.objects.get_or_create(username=username)

il y a bien création de l'utilisateur avec les potentiels espaces rognés.

Par contre la variable tree est réutilisé plus tard par la bibliothèque django-cas-client. L'usage de la bibliothèque est d'également de récupérer le username de l'utilisateur via le xml tree. Ensuite la bibliothèque django-cas-client essaye de récupérer l'utilisateur correspondant s'il existe ou le crée s'il n'existe pas lors de l'authentification de l'utilisateur.
Ainsi dans le cas où le xml tree n'a pas été modifié, le nom d'utilisateur récupéré dans la fonction d'authentification aura gardé ses potentiels espaces ce qui provoquera la création d'un nouvel utilisateur dont le nom d'utilisateur comportera des espaces avant et/ou après le véritable nom d'utilisateur.

Copy link
Contributor Author

@ZephOne ZephOne May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifier seulement username au lieu de username_element est possible si cette pull request est acceptée dans django-cas-client.
Cependant, les mainteneurs n'ont plus l'air d'accepter des pull requests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZephOne mais du coups cette modification risque de créer un nouvel utilisateur egalement non?
à la ligne 93, on modifie l'attribut text de username_element pour ensuite stocker cette meme attribut dans username et à la ligne 97, on get ou create le user avec le username (qui est modifié, sans espace)

Copy link
Contributor

@meschac38700 meschac38700 May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ma proposition est de combiner la ligne 93 et 94 si cela n'a pas de déconvenue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pour bien comprendre je me permet de paraphraser :

ma proposition est de combiner la ligne 93 et 94 si cela n'a pas de déconvenue

Donc le patch suivant :

--- a/pod/authentication/populatedCASbackend.py
+++ b/pod/authentication/populatedCASbackend.py
@@ -90,7 +90,7 @@ SUBTREE = 'SUBTREE'
 def populateUser(tree):
     username_element = tree.find(
         './/{http://www.yale.edu/tp/cas}%s' % AUTH_CAS_USER_SEARCH)
-    username = username_element.text
+    username = username_element.text.strip()
     if CAS_FORCE_LOWERCASE_USERNAME:
         username = username.lower()
     user, user_created = User.objects.get_or_create(username=username)

C'est bien ça @meschac38700 ?

@ZephOne mais du coups cette modification risque de créer un nouvel utilisateur egalement non?

Je ne comprends pas pourquoi ? J'ai peut-être loupé quelque chose. Peux tu m'expliquer @meschac38700 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je comprends que ca puisse être compliquer alors je me rééxplique:
Ta Pull Request propose cette ligne en plus [L93]:

username_element.text = username_element.text.strip()

et juste en dessous on a cec [L94]i:

username = username_element.text

alors moi, je te propose qu'on raccourcir ces 2 lignes en ceci:

username = username_element.text.strip()

@ZephOne

Copy link
Contributor Author

@ZephOne ZephOne May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok donc j'avais bien compris ce que tu me disais.

Si on fait ça, le code :

    user, user_created = User.objects.get_or_create(username=username)

ne créera pas de nouvel utilisateur car ce que tu proposes auras permis de rogner les espaces potentiels préfixant et/ou suffixant le nom de l'utilisateur. C'est bien le fonctionnement attendu.

Toutefois, la variable tree qui est utilisé pour récupéré le nom de l'utilisateur (ligne 91) a été passée à cette fonction populateUser par django-cas-client. La bibliothèque django-cas-client via une variable de configuration propose de customiser la fonction utilisant les données de retour de l'authentification CAS : https://github.com/kstateome/django-cas/#cas-response-callbacks.
Dans le cas de Pod, la fonction custom est populateUser.
Une fois que la fonction populateUser est appelée par la bibliothèque django-cas-client en lui passant le tree xml en argument, la bibliothèque django-cas-client continue l'authentification de l'utilisateur, via la méthode authenticate de la classe CASBackend.
Je poursuis. Dans la méthode authenticate, l'appel (dans le cas de Pod) à la fonction populateUser se fait au travers de la fonction _verify qui pointe vers la fonction _verify_cas3 qui appelle directement la fonction _internal_verify_cas. C'est précisement dans cette fonction _internal_verify_cas qu[est appelée la fonction populateUser de Pod. Cette fonction _internal_verify_cas retourne le username de l'utilisateur. Pour trouver le username de l'utilisateur, elle utilise le tree xml récupéré lors de la réponse envoyé par le serveur CAS. Ce tree s'il n'a pas été modifié entre temps aura toujours un username avec des espaces en préfixe et/ou en suffixe. Dans ce cas la fonction authenticate récupère un username avec des espaces en préfixe et/ou en suffixe.

Après avoir récupéré le username la fonction authenticate va exécuter ce block try-except qui fait à peu près la même chose que la méthode User.objects.get_or_create. Et dans le cas où le tree xml n'a pas été changé et que c'est toujours un utilisateur avec un nom d'utilisateur prefixé et/ou suffixé d'espaces, un nouvel utilisateur est créé. Ce n'est pas ce que l'on veut.

Ce qu'il faut comprendre est qu'il y a deux opération de get_or_create d'utilisateur qui interviennent dans l'authentification CAS :

  • Une dans la fonction populateUser de Pod
  • Une dans la fonction authenticate de django-cas-client
    Il faut s'assurer que pour ces deux opérations, le username est bien rogné de ces espaces.
    La variable qui est utilisé lors de ces deux opérations pour récupérer le username est tree. C'est pour ça que je change la valeur de l'attribut text de l'élément XML qui contient le username de l'utilisateur.
    Si l'on fait seulement ce que tu propose, on récupère bien un username rogné de ses espaces pour l'opération de get_or_create dans populateUser mais le tree XML reste inchangé. Lorsque ce dernier est à nouveau utilisé par la bibliothèque django-cas-client`, cela provoque la création d'un nouvel utilisateur.

Est ce que je suis clair dans mes explications @meschac38700 ?

@ZephOne
Copy link
Contributor Author

ZephOne commented May 28, 2021

I set this PR as draft in order to implement test so that test coverage does not decreased.

@ZephOne ZephOne marked this pull request as ready for review May 28, 2021 07:26
@ZephOne
Copy link
Contributor Author

ZephOne commented May 28, 2021

Finally I unset the draft state as testing the modification would imply testing the strip method. Yet do not hesitate to tell if I'm wrong.

def populateUser(tree):
username_element = tree.find(
'.//{http://www.yale.edu/tp/cas}%s' % AUTH_CAS_USER_SEARCH)
username_element.text = username_element.text.strip()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZephOne mais du coups cette modification risque de créer un nouvel utilisateur egalement non?
à la ligne 93, on modifie l'attribut text de username_element pour ensuite stocker cette meme attribut dans username et à la ligne 97, on get ou create le user avec le username (qui est modifié, sans espace)

def populateUser(tree):
username_element = tree.find(
'.//{http://www.yale.edu/tp/cas}%s' % AUTH_CAS_USER_SEARCH)
username_element.text = username_element.text.strip()
Copy link
Contributor

@meschac38700 meschac38700 May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ma proposition est de combiner la ligne 93 et 94 si cela n'a pas de déconvenue.

def populateUser(tree):
username_element = tree.find(
'.//{http://www.yale.edu/tp/cas}%s' % AUTH_CAS_USER_SEARCH)
username_element.text = username_element.text.strip()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je comprends que ca puisse être compliquer alors je me rééxplique:
Ta Pull Request propose cette ligne en plus [L93]:

username_element.text = username_element.text.strip()

et juste en dessous on a cec [L94]i:

username = username_element.text

alors moi, je te propose qu'on raccourcir ces 2 lignes en ceci:

username = username_element.text.strip()

@ZephOne

@meschac38700
Copy link
Contributor

@ptitloup @ZephOne c'est bon pour moi ! 👍

@ptitloup ptitloup changed the base branch from master to dev May 28, 2021 11:36
@ptitloup
Copy link
Contributor

J'ai changé la basepour passer en dev, il y a un conflit avec le fichier populatedCASbackend.py. @ZephOne est-ce possible de le résoudre que je puisse merger ? Merci

@ZephOne
Copy link
Contributor Author

ZephOne commented May 28, 2021

J'ai changé la basepour passer en dev, il y a un conflit avec le fichier populatedCASbackend.py. @ZephOne est-ce possible de le résoudre que je puisse merger ? Merci

Oui bien sûr.

@ZephOne ZephOne closed this May 28, 2021
@ZephOne ZephOne force-pushed the cas-sso-strip-spaces-around-username branch from 9d54599 to 9dc77af Compare May 28, 2021 13:23
@ZephOne ZephOne reopened this May 28, 2021
@ZephOne
Copy link
Contributor Author

ZephOne commented May 28, 2021

Je vois que vous utilisez black pour le formattage du code. Quel est la version utilisée comme ça je peux le passer sur la nouvelle ligne ajoutée. Même si je suis quasiment sûr que ça ne va rien apporter.

@meschac38700
Copy link
Contributor

Je vois que vous utilisez black pour le formattage du code. Quel est la version utilisée comme ça je peux le passer sur la nouvelle ligne ajoutée. Même si je suis quasiment sûr que ça ne va rien apporter.

https://github.com/EsupPortail/Esup-Pod/blob/dev/.github/workflows/code_formatting.yml#L27

@ZephOne
Copy link
Contributor Author

ZephOne commented May 28, 2021

Je vois que vous utilisez black pour le formattage du code. Quel est la version utilisée comme ça je peux le passer sur la nouvelle ligne ajoutée. Même si je suis quasiment sûr que ça ne va rien apporter.

https://github.com/EsupPortail/Esup-Pod/blob/dev/.github/workflows/code_formatting.yml#L27

Ok la dernière, dans le doute c'est celle que j'ai utilisée.

@meschac38700
Copy link
Contributor

meschac38700 commented May 28, 2021

Je vois que vous utilisez black pour le formattage du code. Quel est la version utilisée comme ça je peux le passer sur la nouvelle ligne ajoutée. Même si je suis quasiment sûr que ça ne va rien apporter.

https://github.com/EsupPortail/Esup-Pod/blob/dev/.github/workflows/code_formatting.yml#L27

Ok la dernière, dans le doute c'est celle que j'ai utilisée.

ceci dit, tu n'as pas à te préoccuper du formattage, celui-ci est automatisé lors du merge request de la branch en Dev

@ptitloup ptitloup merged commit 6fa333e into EsupPortail:dev May 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants