Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,14 @@ Pour lancer la connexion sur un provider en particulier, il suffit d'appeler la

**OpenID et OpenIDConnect**.

- `group_claim_name` (string) : nom du champs retournée par le fournisseur d'identité dans lequel se trouve la liste de groupes auquel l'utilisateur appartient (par défaut : "groups").
- `ISSUER` (string) : URL du fournisseur d'identité
- `CLIENT_ID` (string) : Identifiant publique de l'application auprès du fournisseur d'identité.
- `CLIENT_SECRET` (string) : Clé secrete connue uniquement par l'application et le fournisseur d'identité.
- `group_claim_name` (string) : nom du champs retournée par le fournisseur d'identités dans lequel se trouve la liste de groupes auquel l'utilisateur appartient (par défaut : "groups").
- `ISSUER` (string) : URL du fournisseur d'identités
- `CLIENT_ID` (string) : Identifiant publique de l'application auprès du fournisseur d'identités.
- `CLIENT_SECRET` (string) : Clé secrete connue uniquement par l'application et le fournisseur d'identités.
- `IDENTIFIER_FIELD` (string): Nom du champs contenant l'identifiant de l'utilisateur.
- `RECONCILIATE_ATTR` (string): Nom du champs qui servira à la réconciliation des utilisateurs existant (par défaut: "email").
- `FIELDS_TO_OVERRIDE` (List(string)): Nom des champs qui seront écrasés lors d'une connexion. Ces champs prendront la valeur existante dans le provider (par défaut, nom, prénom et email). Les valeurs possibles sont les suivantes : "id_role", "identifiant", "nom_role", "prenom_role", "email","active"
- `CODE_CHALLENGE_METHOD` (string): Ne devrait pas être modifié. Permet de configurer le type de challenge (par défaut: "S256" mais peut valoir plain si contraintes legacy).

**UsersHub-authentification-module**

Expand Down
3 changes: 2 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

**🚀 Nouveautés**

- Ajout d'un paramètre is_secondary aux fournisseurs d'identité (#144 par @IdrissaD)
- Ajout d'un paramètre `is_secondary` dans la classe `Authentication` (#144 par @IdrissaD)
- Ajout du paramètre `FIELDS_TO_OVERRIDE` au connecteur OpenID (#148 par @christophe-ramet)


**🐛 Corrections**
Expand Down
5 changes: 5 additions & 0 deletions src/pypnusershub/auth/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ def insert_or_update_role(
user_dict: dict,
reconciliate_attr="email",
source_groups: List[int] = [],
fields_to_update: List[str] = [],
) -> models.User:
"""
Insert or update a role (also add groups if provided)
Expand All @@ -201,6 +202,8 @@ def insert_or_update_role(
Attribute used to reconciliate existing users
source_groups: List[str], default=[]
List of group names to compare with existing groups defined in the group_mapping properties of the provider
fields_to_update: List[str]
List of fields that should be updated if user already exists. If left empty, all fields will be updated.

Returns
-------
Expand Down Expand Up @@ -237,6 +240,8 @@ def insert_or_update_role(
user_exists.providers.append(provider)

for attr_key, attr_value in user_dict.items():
if fields_to_update and attr_key not in fields_to_update:
continue
setattr(user_exists, attr_key, attr_value)
db.session.commit()
return user_exists
Expand Down
34 changes: 29 additions & 5 deletions src/pypnusershub/auth/providers/openid_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@
from pypnusershub.auth import Authentication, ProviderConfigurationSchema, oauth
from pypnusershub.db import db, models
from werkzeug.exceptions import Unauthorized
from enum import Enum


class UserColumns(str, Enum):
"""
Enum listing the columns from the User model that can be modified.
"""

ID_ROLE = "id_role"
IDENTIFIANT = "identifiant"
NOM_ROLE = "nom_role"
PRENOM_ROLE = "prenom_role"
EMAIL = "email"
ACTIVE = "active"


class OpenIDProvider(Authentication):
Expand Down Expand Up @@ -38,14 +52,14 @@ def authorize(self):
session["openid_token_resp"] = token
user_info = token["userinfo"]
new_user = {
"identifiant": user_info.get(
UserColumns.IDENTIFIANT: user_info.get(
self.identifier_field,
f"{user_info['given_name'].lower()}.{user_info['family_name'].lower()}",
),
"email": user_info["email"],
"prenom_role": user_info["given_name"],
"nom_role": user_info["family_name"],
"active": True,
UserColumns.EMAIL: user_info["email"],
UserColumns.PRENOM_ROLE: user_info["given_name"],
UserColumns.NOM_ROLE: user_info["family_name"],
UserColumns.ACTIVE: True,
}
source_groups = (
user_info[self.group_claim_name]
Expand All @@ -56,6 +70,7 @@ def authorize(self):
new_user,
source_groups=source_groups,
reconciliate_attr=self.reconciliate_attr,
fields_to_update=self.fields_to_override,
)
db.session.commit()
return user
Expand Down Expand Up @@ -88,6 +103,14 @@ class OpenIDProviderConfiguration(ProviderConfigurationSchema):
load_default="S256",
validate=fields.validate.OneOf(["plain", "S256"]),
)
FIELDS_TO_OVERRIDE = fields.List(
fields.Enum(UserColumns, by_value=True),
load_default=[
UserColumns.NOM_ROLE,
UserColumns.PRENOM_ROLE,
UserColumns.EMAIL,
],
)

super().configure(configuration)
try:
Expand All @@ -113,6 +136,7 @@ class OpenIDProviderConfiguration(ProviderConfigurationSchema):
self.group_claim_name = configuration["group_claim_name"]
self.identifier_field = configuration["IDENTIFIER_FIELD"]
self.reconciliate_attr = configuration["RECONCILIATE_ATTR"]
self.fields_to_override = configuration["FIELDS_TO_OVERRIDE"]


class OpenIDConnectProvider(OpenIDProvider):
Expand Down
8 changes: 7 additions & 1 deletion src/pypnusershub/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,13 @@ def group_and_users(app, applications, profils):
db.session.add(group1)
group2 = User(groupe=True, identifiant="group2")
db.session.add(group2)
user1 = User(groupe=False, identifiant="user_of_group1")
user1 = User(
groupe=False,
identifiant="user_of_group1",
email="user1@email.fr",
prenom_role="prenom",
nom_role="nom",
)
user1.password = "admin"
user_no_group = User(groupe=False, identifiant="user2")
user1.groups.append(group1)
Expand Down
28 changes: 28 additions & 0 deletions src/pypnusershub/tests/test_utilisateurs.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,34 @@ def test_insert_or_update_role_grp_reconcialiation(
user_group_id = map(lambda g: g.id_role, user.groups)
assert set(user_group_id) == {group_and_users["group1"].id_role}

def test_insert_or_update_with_fields_to_update(
self, provider_instance, group_and_users
):
"""
Test that the fields_to_update parameter works as expected and, if filled it only updates the fields specified in the parameter.

"""
user = group_and_users["user1"]
user_to_reconcialite = {
"email": user.email,
"nom_role": "new nom",
"prenom_role": "new prénom",
}
user = provider_instance.insert_or_update_role(
user_dict=user_to_reconcialite,
reconciliate_attr="email",
fields_to_update=["nom_role"],
)

assert user.prenom_role == "prenom"
assert user.nom_role == "new nom"

user = provider_instance.insert_or_update_role(
user_dict=user_to_reconcialite, reconciliate_attr="email"
)
assert user.prenom_role == "new prénom"
assert user.nom_role == "new nom"

def test_insert_organisme(self):
organism = {
"nom_organisme": "test",
Expand Down
Loading