Skip to content

Commit f452769

Browse files
committed
fix: fallback to email local part when user name is missing
- Prevents 'NoneType' error by checking for missing user name before calling split() - Uses primary email's local part (before '@') as first_name if user name is not provided Signed-off-by: Eugene <[email protected]>
1 parent 732a0f0 commit f452769

File tree

1 file changed

+42
-38
lines changed

1 file changed

+42
-38
lines changed

charts/airflow/docs/faq/security/ldap-oauth.md

Lines changed: 42 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
# Integrate Airflow with LDAP or OAUTH
66

77
> 🟥 __Warning__ 🟥
8-
>
8+
>
99
> The `AUTH_ROLES_MAPPING` feature requires `Flask-Appbuilder>=3.2.0`.
1010
> Starting from Airflow 2.0.2, `Flask-Appbuilder>=3.2.0` is included by default,
1111
> older versions of airflow will require you to [manually install](../configuration/extra-python-packages.md) `Flask-AppBuilder>=3.2.0`.
1212
1313
> 🟦 __Tip__ 🟦
1414
>
1515
> After integrating with LDAP or OAUTH, you should:
16-
>
16+
>
1717
> 1. Set the `airflow.users` value to `[]`
1818
> 2. Manually delete any previously created users (with the airflow WebUI)
1919
@@ -56,22 +56,22 @@ web:
5656
# only needed for airflow 1.10
5757
#from airflow import configuration as conf
5858
#SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN")
59-
59+
6060
AUTH_TYPE = AUTH_LDAP
6161
AUTH_LDAP_SERVER = "ldap://ldap.example.com"
6262
AUTH_LDAP_USE_TLS = False
63-
63+
6464
# registration configs
6565
AUTH_USER_REGISTRATION = True # allow users who are not already in the FAB DB
6666
AUTH_USER_REGISTRATION_ROLE = "Public" # this role will be given in addition to any AUTH_ROLES_MAPPING
6767
AUTH_LDAP_FIRSTNAME_FIELD = "givenName"
6868
AUTH_LDAP_LASTNAME_FIELD = "sn"
6969
AUTH_LDAP_EMAIL_FIELD = "mail" # if null in LDAP, email is set to: "{username}@email.notfound"
70-
70+
7171
# bind username (for password validation)
7272
AUTH_LDAP_USERNAME_FORMAT = "uid=%s,ou=users,dc=example,dc=com" # %s is replaced with the provided username
7373
# AUTH_LDAP_APPEND_DOMAIN = "example.com" # bind usernames will look like: {USERNAME}@example.com
74-
74+
7575
# search configs
7676
AUTH_LDAP_SEARCH = "ou=users,dc=example,dc=com" # the LDAP search base (if non-empty, a search will ALWAYS happen)
7777
AUTH_LDAP_UID_FIELD = "uid" # the username field
@@ -81,13 +81,13 @@ web:
8181
"cn=airflow_users,ou=groups,dc=example,dc=com": ["User"],
8282
"cn=airflow_admins,ou=groups,dc=example,dc=com": ["Admin"],
8383
}
84-
84+
8585
# the LDAP user attribute which has their role DNs
8686
AUTH_LDAP_GROUP_FIELD = "memberOf"
87-
87+
8888
# if we should replace ALL the user's roles each login, or only on registration
8989
AUTH_ROLES_SYNC_AT_LOGIN = True
90-
90+
9191
# force users to re-auth after 30min of inactivity (to keep roles in sync)
9292
PERMANENT_SESSION_LIFETIME = 1800
9393
```
@@ -115,18 +115,18 @@ web:
115115
# only needed for airflow 1.10
116116
#from airflow import configuration as conf
117117
#SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN")
118-
118+
119119
AUTH_TYPE = AUTH_LDAP
120120
AUTH_LDAP_SERVER = "ldap://ldap.example.com"
121121
AUTH_LDAP_USE_TLS = False
122-
122+
123123
# registration configs
124124
AUTH_USER_REGISTRATION = True # allow users who are not already in the FAB DB
125125
AUTH_USER_REGISTRATION_ROLE = "Public" # this role will be given in addition to any AUTH_ROLES_MAPPING
126126
AUTH_LDAP_FIRSTNAME_FIELD = "givenName"
127127
AUTH_LDAP_LASTNAME_FIELD = "sn"
128128
AUTH_LDAP_EMAIL_FIELD = "mail" # if null in LDAP, email is set to: "{username}@email.notfound"
129-
129+
130130
# search configs
131131
AUTH_LDAP_SEARCH = "ou=users,dc=example,dc=com" # the LDAP search base
132132
AUTH_LDAP_UID_FIELD = "uid" # the username field
@@ -138,13 +138,13 @@ web:
138138
"cn=airflow_users,ou=groups,dc=example,dc=com": ["User"],
139139
"cn=airflow_admins,ou=groups,dc=example,dc=com": ["Admin"],
140140
}
141-
141+
142142
# the LDAP user attribute which has their role DNs
143143
AUTH_LDAP_GROUP_FIELD = "memberOf"
144-
144+
145145
# if we should replace ALL the user's roles each login, or only on registration
146146
AUTH_ROLES_SYNC_AT_LOGIN = True
147-
147+
148148
# force users to re-auth after 30min of inactivity (to keep roles in sync)
149149
PERMANENT_SESSION_LIFETIME = 1800
150150
```
@@ -185,31 +185,35 @@ web:
185185
# Custom AirflowSecurityManager
186186
#######################################
187187
from airflow.www.security import AirflowSecurityManager
188-
188+
189189
class CustomSecurityManager(AirflowSecurityManager):
190190
def get_oauth_user_info(self, provider, resp):
191191
if provider == "github":
192192
user_data = self.appbuilder.sm.oauth_remotes[provider].get("user").json()
193193
emails_data = self.appbuilder.sm.oauth_remotes[provider].get("user/emails").json()
194194
teams_data = self.appbuilder.sm.oauth_remotes[provider].get("user/teams").json()
195-
196-
# unpack the user's name
197-
first_name = ""
198-
last_name = ""
199-
name = user_data.get("name", "").split(maxsplit=1)
200-
if len(name) == 1:
201-
first_name = name[0]
202-
elif len(name) == 2:
203-
first_name = name[0]
204-
last_name = name[1]
205-
195+
206196
# unpack the user's email
207197
email = ""
208198
for email_data in emails_data:
209199
if email_data["primary"]:
210200
email = email_data["email"]
211201
break
212-
202+
203+
# unpack the user's name
204+
first_name = ""
205+
last_name = ""
206+
raw_name = user_data.get("name")
207+
if raw_name:
208+
name_parts = raw_name.split(maxsplit=1)
209+
if len(name_parts) == 1:
210+
first_name = name_parts[0]
211+
elif len(name_parts) == 2:
212+
first_name, last_name = name_parts
213+
elif email:
214+
# set user name from email address
215+
first_name = email.split("@")[0]
216+
213217
# unpack the user's teams as role_keys
214218
# NOTE: each role key will be "my-github-org/my-team-name"
215219
role_keys = []
@@ -218,7 +222,7 @@ web:
218222
team_slug = team_data["slug"]
219223
team_ref = team_org + "/" + team_slug
220224
role_keys.append(team_ref)
221-
225+
222226
return {
223227
"username": "github_" + user_data.get("login", ""),
224228
"first_name": first_name,
@@ -228,23 +232,23 @@ web:
228232
}
229233
else:
230234
return {}
231-
235+
232236
#######################################
233237
# Actual `webserver_config.py`
234238
#######################################
235239
from flask_appbuilder.security.manager import AUTH_OAUTH
236-
240+
237241
# only needed for airflow 1.10
238242
#from airflow import configuration as conf
239243
#SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN")
240-
244+
241245
AUTH_TYPE = AUTH_OAUTH
242246
SECURITY_MANAGER_CLASS = CustomSecurityManager
243-
247+
244248
# registration configs
245249
AUTH_USER_REGISTRATION = True # allow users who are not already in the FAB DB
246250
AUTH_USER_REGISTRATION_ROLE = "Public" # this role will be given in addition to any AUTH_ROLES_MAPPING
247-
251+
248252
# the list of providers which the user can choose from
249253
OAUTH_PROVIDERS = [
250254
{
@@ -261,16 +265,16 @@ web:
261265
},
262266
},
263267
]
264-
268+
265269
# a mapping from the values of `userinfo["role_keys"]` to a list of FAB roles
266270
AUTH_ROLES_MAPPING = {
267271
"my-github-org/airflow-users-team": ["User"],
268272
"my-github-org/airflow-admin-team": ["Admin"],
269273
}
270-
274+
271275
# if we should replace ALL the user's roles each login, or only on registration
272276
AUTH_ROLES_SYNC_AT_LOGIN = True
273-
277+
274278
# force users to re-auth after 30min of inactivity (to keep roles in sync)
275279
PERMANENT_SESSION_LIFETIME = 1800
276280
```
@@ -337,4 +341,4 @@ web:
337341
PERMANENT_SESSION_LIFETIME = 1800
338342
```
339343
340-
</details>
344+
</details>

0 commit comments

Comments
 (0)