Skip to content

Commit a5e679d

Browse files
author
Stanislav Lysikov
committed
add AUTH_LDAP_USE_NESTED_GROUPS_FOR_ROLES flag which enable getting nested groups from ms active directory
1 parent ba63c5c commit a5e679d

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

docs/config.rst

+6-4
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ Use config.py to configure the following parameters. By default it will use SQLL
209209
| | | |
210210
| | It authenticates with "format-userexample".| |
211211
+----------------------------------------+--------------------------------------------+-----------+
212+
| AUTH_LDAP_USE_NESTED_GROUPS_FOR_ROLES | Get users nested groups from LDAP(MS AD) | No |
213+
+----------------------------------------+--------------------------------------------+-----------+
212214
| AUTH_ROLE_ADMIN | Configure the name of the admin role. | No |
213215
+----------------------------------------+--------------------------------------------+-----------+
214216
| AUTH_ROLE_PUBLIC | Special Role that holds the public | No |
@@ -333,10 +335,10 @@ It should be a long random bytes or str. For example, copy the output of this to
333335

334336
Using config.py
335337
---------------
336-
338+
337339
My favorite way, and the one I advise if you are building a medium to large size application
338340
is to place all your configuration keys on a config.py file
339-
341+
340342
Next you only have to import them to the Flask app object, like this
341343
::
342344

@@ -351,10 +353,10 @@ Take a look at the skeleton `config.py <https://github.com/dpgaspar/Flask-AppBui
351353
Using JMESPath to map user registration role
352354
--------------------------------------------
353355

354-
If user self registration is enabled and ``AUTH_USER_REGISTRATION_ROLE_JMESPATH`` is set, it is
356+
If user self registration is enabled and ``AUTH_USER_REGISTRATION_ROLE_JMESPATH`` is set, it is
355357
used as a `JMESPath <http://jmespath.org/>`_ expression to evalate user registration role. The input
356358
values is ``userinfo`` dict, returned by ``get_oauth_user_info`` function of Security Manager.
357-
Usage of JMESPath expressions requires `jmespath <https://pypi.org/project/jmespath/>`_ package
359+
Usage of JMESPath expressions requires `jmespath <https://pypi.org/project/jmespath/>`_ package
358360
to be installed.
359361

360362
In case of Google OAuth, userinfo contains user's email that can be used to map some users as admins

flask_appbuilder/security/manager.py

+36-2
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ def __init__(self, appbuilder):
257257
app.config.setdefault("AUTH_LDAP_FIRSTNAME_FIELD", "givenName")
258258
app.config.setdefault("AUTH_LDAP_LASTNAME_FIELD", "sn")
259259
app.config.setdefault("AUTH_LDAP_EMAIL_FIELD", "mail")
260+
# Nested groups options
261+
app.config.setdefault("AUTH_LDAP_USE_NESTED_GROUPS_FOR_ROLES", False)
260262

261263
# Rate limiting
262264
app.config.setdefault("AUTH_RATE_LIMITED", False)
@@ -495,6 +497,10 @@ def auth_ldap_tls_certfile(self):
495497
def auth_ldap_tls_keyfile(self):
496498
return self.appbuilder.get_app.config["AUTH_LDAP_TLS_KEYFILE"]
497499

500+
@property
501+
def auth_ldap_use_nested_groups_for_roles(self):
502+
return self.appbuilder.get_app.config["AUTH_LDAP_USE_NESTED_GROUPS_FOR_ROLES"]
503+
498504
@property
499505
def openid_providers(self):
500506
return self.appbuilder.get_app.config["OPENID_PROVIDERS"]
@@ -963,11 +969,39 @@ def _search_ldap(self, ldap, con, username):
963969
user_dn = search_result[0][0]
964970
# extract the other attributes
965971
user_info = search_result[0][1]
966-
# return
967-
return user_dn, user_info
968972
except (IndexError, NameError):
969973
return None, None
970974

975+
# get nested groups for user
976+
if self.auth_ldap_use_nested_groups_for_roles:
977+
log.debug("Nested groups for LDAP enabled.")
978+
# filter for microsoft active directory only
979+
nested_groups_filter_str = (
980+
f"(&(objectCategory=Group)(member:1.2.840.113556.1.4.1941:={user_dn}))"
981+
)
982+
nested_groups_request_fields = ["cn"]
983+
984+
nested_groups_search_result = con.search_s(
985+
self.auth_ldap_search,
986+
ldap.SCOPE_SUBTREE,
987+
nested_groups_filter_str,
988+
nested_groups_request_fields,
989+
)
990+
log.debug(
991+
"LDAP search for nested groups returned: %s",
992+
nested_groups_search_result,
993+
)
994+
995+
nested_groups = [
996+
x[0].encode() for x in nested_groups_search_result if x[0] is not None
997+
]
998+
log.debug("LDAP nested groups for users: %s", nested_groups)
999+
1000+
user_info[self.auth_ldap_group_field] = nested_groups
1001+
1002+
# return
1003+
return user_dn, user_info
1004+
9711005
def _ldap_calculate_user_roles(
9721006
self, user_attributes: Dict[str, bytes]
9731007
) -> List[str]:

0 commit comments

Comments
 (0)