From 0c1701314ad7945b87b0c5b5499303e7011c8ede Mon Sep 17 00:00:00 2001 From: sayali Date: Wed, 10 Feb 2021 12:03:46 -0800 Subject: [PATCH 1/3] User memberships --- docs/administration.rst | 27 +++++++++++++++++ docs/developer/plugins/index.rst | 11 +++++++ lemur/auth/views.py | 50 ++++++++++++++++++++++++++++++-- lemur/plugins/bases/__init__.py | 1 + lemur/plugins/bases/tls.py | 20 +++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 lemur/plugins/bases/tls.py diff --git a/docs/administration.rst b/docs/administration.rst index 3f282369..3623f311 100644 --- a/docs/administration.rst +++ b/docs/administration.rst @@ -712,6 +712,33 @@ For more information about how to use social logins, see: `Satellizer """ +import json + import jwt import base64 import requests @@ -23,7 +25,7 @@ from lemur.roles import service as role_service from lemur.logs import service as log_service from lemur.auth.service import create_token, fetch_token_header, get_rsa_public_key from lemur.auth import ldap - +from lemur.plugins.base import plugins mod = Blueprint("auth", __name__) api = Api(mod) @@ -138,6 +140,44 @@ def retrieve_user(user_api_url, access_token): return user, profile +def retrieve_user_memberships(user_api_url, user_membership_api_url, access_token): + user, profile = retrieve_user(user_api_url, access_token) + + if user_membership_api_url is None: + return user, profile + """ + Potentially, below code can be made more generic i.e., plugin driven. Unaware of the usage of this + code across the community, current implementation is config driven. Without user_membership_api_url + configured, it is backward compatible. + """ + + # put user id in url + user_membership_api_url = user_membership_api_url.replace("%user_id%", profile["userId"]) + + headers = {"Content-Type": "application/json"} + data = {"relation": "DIRECT_ONLY", "groupFilter": {"type": "GOOGLE"}, "size": 500} + + tls_provider = plugins.get(current_app.config.get("PING_USER_MEMBERSHIP_TLS_PROVIDER")) + + # retrieve information about the current user + session = tls_provider.session(current_app.config.get("PING_USER_MEMBERSHIP_SERVICE")) + r = session.post(user_membership_api_url, data=json.dumps(data), headers=headers) + + user_membership = {"email": profile["email"], + "thumbnailPhotoUrl": profile["thumbnailPhotoUrl"], + "googleGroups": []} + + if r.status_code == 200: + response = r.json() + membership_details = response["data"] + for membership in membership_details: + user_membership["googleGroups"].append(membership["membership"]["name"]) + + return user, user_membership + + current_app.logger.error(f"Response Code:{r.status_code} {r.text}") + + def create_user_roles(profile): """Creates new roles based on profile information. @@ -375,7 +415,6 @@ class Ping(Resource): # you can either discover these dynamically or simply configure them access_token_url = current_app.config.get("PING_ACCESS_TOKEN_URL") - user_api_url = current_app.config.get("PING_USER_API_URL") secret = current_app.config.get("PING_SECRET") @@ -391,7 +430,12 @@ class Ping(Resource): error_code = validate_id_token(id_token, args["clientId"], jwks_url) if error_code: return error_code - user, profile = retrieve_user(user_api_url, access_token) + + user, profile = retrieve_user_memberships( + current_app.config.get("PING_USER_API_URL"), + current_app.config.get("PING_USER_MEMBERSHIP_URL"), + access_token + ) roles = create_user_roles(profile) update_user(user, profile, roles) diff --git a/lemur/plugins/bases/__init__.py b/lemur/plugins/bases/__init__.py index 9ce6289b..71ee2eb3 100644 --- a/lemur/plugins/bases/__init__.py +++ b/lemur/plugins/bases/__init__.py @@ -3,3 +3,4 @@ from .issuer import IssuerPlugin # noqa from .source import SourcePlugin # noqa from .notification import NotificationPlugin, ExpirationNotificationPlugin # noqa from .export import ExportPlugin # noqa +from .tls import TLSPlugin # noqa diff --git a/lemur/plugins/bases/tls.py b/lemur/plugins/bases/tls.py new file mode 100644 index 00000000..509ecd62 --- /dev/null +++ b/lemur/plugins/bases/tls.py @@ -0,0 +1,20 @@ +""" +.. module: lemur.plugins.bases.tls + :platform: Unix + :copyright: (c) 2021 by Netflix Inc., see AUTHORS for more + :license: Apache, see LICENSE for more details. + +.. moduleauthor:: Sayali Charhate +""" +from lemur.plugins.base import Plugin + + +class TLSPlugin(Plugin): + """ + This is the base class from which all supported + tls session providers will inherit from. + """ + type = "tls" + + def session(self, server_application): + raise NotImplementedError From a4f3ffa2d8fa4ed196b4ee449708b00c491352a3 Mon Sep 17 00:00:00 2001 From: sayali Date: Wed, 10 Feb 2021 12:12:52 -0800 Subject: [PATCH 2/3] never set admin third party --- lemur/auth/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lemur/auth/views.py b/lemur/auth/views.py index 9106665d..3ba3bbb3 100644 --- a/lemur/auth/views.py +++ b/lemur/auth/views.py @@ -196,7 +196,7 @@ def create_user_roles(profile): description="This is a google group based role created by Lemur", third_party=True, ) - if not role.third_party: + if (group != 'admin') and (not role.third_party): role = role_service.set_third_party(role.id, third_party_status=True) roles.append(role) else: From c71f3bfb5c557d15115703e21f1f59ae57418f2b Mon Sep 17 00:00:00 2001 From: sayali Date: Wed, 10 Feb 2021 13:14:31 -0800 Subject: [PATCH 3/3] membership API pagination --- lemur/auth/views.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lemur/auth/views.py b/lemur/auth/views.py index 3ba3bbb3..83b67f6a 100644 --- a/lemur/auth/views.py +++ b/lemur/auth/views.py @@ -150,32 +150,35 @@ def retrieve_user_memberships(user_api_url, user_membership_api_url, access_toke code across the community, current implementation is config driven. Without user_membership_api_url configured, it is backward compatible. """ + tls_provider = plugins.get(current_app.config.get("PING_USER_MEMBERSHIP_TLS_PROVIDER")) # put user id in url user_membership_api_url = user_membership_api_url.replace("%user_id%", profile["userId"]) + session = tls_provider.session(current_app.config.get("PING_USER_MEMBERSHIP_SERVICE")) headers = {"Content-Type": "application/json"} data = {"relation": "DIRECT_ONLY", "groupFilter": {"type": "GOOGLE"}, "size": 500} - - tls_provider = plugins.get(current_app.config.get("PING_USER_MEMBERSHIP_TLS_PROVIDER")) - - # retrieve information about the current user - session = tls_provider.session(current_app.config.get("PING_USER_MEMBERSHIP_SERVICE")) - r = session.post(user_membership_api_url, data=json.dumps(data), headers=headers) - user_membership = {"email": profile["email"], "thumbnailPhotoUrl": profile["thumbnailPhotoUrl"], "googleGroups": []} + while True: + # retrieve information about the current user memberships + r = session.post(user_membership_api_url, data=json.dumps(data), headers=headers) - if r.status_code == 200: - response = r.json() - membership_details = response["data"] - for membership in membership_details: - user_membership["googleGroups"].append(membership["membership"]["name"]) + if r.status_code == 200: + response = r.json() + membership_details = response["data"] + for membership in membership_details: + user_membership["googleGroups"].append(membership["membership"]["name"]) - return user, user_membership - - current_app.logger.error(f"Response Code:{r.status_code} {r.text}") + if "nextPageToken" in response and response["nextPageToken"]: + data["nextPageToken"] = response["nextPageToken"] + else: + break + else: + current_app.logger.error(f"Response Code:{r.status_code} {r.text}") + break + return user, user_membership def create_user_roles(profile):