Merge pull request #3414 from charhate/membership

Configurable user memberships endpoint
This commit is contained in:
charhate 2021-02-10 15:00:05 -08:00 committed by GitHub
commit 6fd7684d2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 4 deletions

View File

@ -712,6 +712,33 @@ For more information about how to use social logins, see: `Satellizer <https://g
PING_AUTH_ENDPOINT = "https://<yourpingserver>/oauth2/authorize" PING_AUTH_ENDPOINT = "https://<yourpingserver>/oauth2/authorize"
.. data:: PING_USER_MEMBERSHIP_URL
:noindex:
An optional additional endpoint to learn membership details post the user validation.
::
PING_USER_MEMBERSHIP_URL = "https://<yourmembershipendpoint>"
.. data:: PING_USER_MEMBERSHIP_TLS_PROVIDER
:noindex:
A custom TLS session provider plugin name
::
PING_USER_MEMBERSHIP_TLS_PROVIDER = "slug-name"
.. data:: PING_USER_MEMBERSHIP_SERVICE
:noindex:
Membership service name used by PING_USER_MEMBERSHIP_TLS_PROVIDER to create a session
::
PING_USER_MEMBERSHIP_SERVICE = "yourmembershipservice"
.. data:: OAUTH2_SECRET .. data:: OAUTH2_SECRET
:noindex: :noindex:

View File

@ -285,6 +285,17 @@ The `ExportPlugin` object requires the implementation of one function::
Support of various formats sometimes relies on external tools system calls. Always be mindful of sanitizing any input to these calls. Support of various formats sometimes relies on external tools system calls. Always be mindful of sanitizing any input to these calls.
Custom TLS Provider
------
Managing TLS at the enterprise scale could be hard and often organizations offer custom wrapper implementations. It could
be ideal to use those while making calls to internal services. The `TLSPlugin` would help to achieve this. It requires the
implementation of one function which creates a TLS session::
def session(self, server_application):
# return active session
Testing Testing
======= =======

View File

@ -5,6 +5,8 @@
:license: Apache, see LICENSE for more details. :license: Apache, see LICENSE for more details.
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com> .. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
""" """
import json
import jwt import jwt
import base64 import base64
import requests import requests
@ -23,7 +25,7 @@ from lemur.roles import service as role_service
from lemur.logs import service as log_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.service import create_token, fetch_token_header, get_rsa_public_key
from lemur.auth import ldap from lemur.auth import ldap
from lemur.plugins.base import plugins
mod = Blueprint("auth", __name__) mod = Blueprint("auth", __name__)
api = Api(mod) api = Api(mod)
@ -138,6 +140,47 @@ def retrieve_user(user_api_url, access_token):
return user, profile 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.
"""
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}
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 "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): def create_user_roles(profile):
"""Creates new roles based on profile information. """Creates new roles based on profile information.
@ -156,7 +199,7 @@ def create_user_roles(profile):
description="This is a google group based role created by Lemur", description="This is a google group based role created by Lemur",
third_party=True, 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) role = role_service.set_third_party(role.id, third_party_status=True)
roles.append(role) roles.append(role)
else: else:
@ -375,7 +418,6 @@ class Ping(Resource):
# you can either discover these dynamically or simply configure them # you can either discover these dynamically or simply configure them
access_token_url = current_app.config.get("PING_ACCESS_TOKEN_URL") 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") secret = current_app.config.get("PING_SECRET")
@ -391,7 +433,12 @@ class Ping(Resource):
error_code = validate_id_token(id_token, args["clientId"], jwks_url) error_code = validate_id_token(id_token, args["clientId"], jwks_url)
if error_code: if error_code:
return 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) roles = create_user_roles(profile)
update_user(user, profile, roles) update_user(user, profile, roles)

View File

@ -3,3 +3,4 @@ from .issuer import IssuerPlugin # noqa
from .source import SourcePlugin # noqa from .source import SourcePlugin # noqa
from .notification import NotificationPlugin, ExpirationNotificationPlugin # noqa from .notification import NotificationPlugin, ExpirationNotificationPlugin # noqa
from .export import ExportPlugin # noqa from .export import ExportPlugin # noqa
from .tls import TLSPlugin # noqa

View File

@ -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 <scharhate@netflix.com>
"""
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