From 0ccc9d3575e02153571d71f26834b96cb8f6dd54 Mon Sep 17 00:00:00 2001
From: kaiyou <pierre@jaury.eu>
Date: Tue, 17 Mar 2020 12:26:17 +0100
Subject: [PATCH] Add the ability to configure special OIDC mappings

---
 hiboo/sso/forms.py |  6 ++++++
 hiboo/sso/oidc.py  | 36 +++++++++++++++++++++++++-----------
 2 files changed, 31 insertions(+), 11 deletions(-)

diff --git a/hiboo/sso/forms.py b/hiboo/sso/forms.py
index 9f76b3b3..98a7994a 100644
--- a/hiboo/sso/forms.py
+++ b/hiboo/sso/forms.py
@@ -39,4 +39,10 @@ class OIDCForm(flask_wtf.FlaskForm):
             ("id_token token", _("Id token and token"))
         ]
     )
+    special_mappings = fields.SelectMultipleField(
+        _('Enabled special claim mappings'), choices=[
+            ("mask_sub_uuid", _("Mask the profile uuid")),
+            ("original_email", _("Return the actual user email"))
+        ]
+    )
     submit = fields.SubmitField(_('Submit'))
diff --git a/hiboo/sso/oidc.py b/hiboo/sso/oidc.py
index 7019dce1..dd9a77f1 100644
--- a/hiboo/sso/oidc.py
+++ b/hiboo/sso/oidc.py
@@ -14,6 +14,7 @@ from hiboo import models, utils, profile
 
 import flask
 import time
+import inspect
 
 
 class Config(object):
@@ -36,7 +37,8 @@ class Config(object):
             "token_endpoint_auth_method": form.token_endpoint_auth_method.data,
             "redirect_uris": [form.redirect_uri.data],
             "grant_types": form.grant_types.data,
-            "response_types": form.response_types.data
+            "response_types": form.response_types.data,
+            "special_mappings": form.special_mappings.data
         })
         cls.update_client(service)
 
@@ -47,7 +49,8 @@ class Config(object):
             token_endpoint_auth_method=service.config.get("token_endpoint_auth_method"),
             redirect_uri=service.config.get("redirect_uris", [""])[0],
             grant_types=service.config.get("grant_types", ["authorization_code"]),
-            response_types=service.config.get("response_types", ["code"])
+            response_types=service.config.get("response_types", ["code"]),
+            special_mappings=service.config.get("special_mappings", [])
         )
 
     @classmethod
@@ -70,11 +73,11 @@ class AuthorizationCodeMixin(object):
     # Authorization code object for redis storage
     AuthorizationCode = type("AuthorizationCode", (utils.SerializableObj, sqla_oauth2.OAuth2AuthorizationCodeMixin), {})
 
-    def create_authorization_code(self, client, grant_user, request):
+    def create_authorization_code(self, client, profile, request):
         obj = AuthorizationCodeMixin.AuthorizationCode(
             code=security.generate_token(48), nonce=request.data.get("nonce") or "",
             client_id=client.client_id, redirect_uri=request.redirect_uri,
-            scope=request.scope, user_id=grant_user.uuid,
+            scope=request.scope, user_id=profile.uuid,
             auth_time=int(time.time())
         )
         utils.redis.hmset("code:{}".format(obj.code), obj.serialize())
@@ -112,8 +115,8 @@ class OpenIDMixin(object):
     def get_jwt_config(self, grant=None):
         return self.get_client().get_jwt_config()
 
-    def generate_user_info(self, user, scope):
-        return self.get_client().generate_user_info(user, scope)
+    def generate_user_info(self, profile, scope):
+        return self.get_client().generate_user_info(profile, scope)
 
 
 class Client(sqla_oauth2.OAuth2ClientMixin):
@@ -160,13 +163,24 @@ class Client(sqla_oauth2.OAuth2ClientMixin):
             'exp': 3600,
         }
 
-    def generate_user_info(self, user, scope):
+    def generate_user_info(self, profile, scope):
         """ User info generation function used by the oidc code mixin and the userinfo endpoint
         """
-        return oidc.UserInfo(
-            sub=user.uuid, name=user.username, prefered_username=user.username,
-            login=user.username, email=user.email
-        )
+        special_mappings = self.service.config.get("special_mappings", [])
+        claims = dict()    
+        if "openid" in scope:
+            claims.update(
+                sub=profile.username if "mask_sub_uuid" in special_mappings else profile.uuid,
+                name=profile.username,
+                preferred_username=profile.username,
+                login=profile.username
+            )
+        if "email" in scope:
+            user_email = profile.user.contact.get("email")
+            claims.update(
+                email=user_email if "original_email" in special_mappings else profile.email
+            )
+        return oidc.UserInfo(**claims)
 
     def generate_token(self, client, grant_type, user=None, scope=None, expires_in=None, include_refresh_token=False):
         """ Specific token generation function to help keep track of the profile associated with a token
-- 
GitLab