Show More
@@ -427,6 +427,74 b' reverse-proxy setup with basic auth:' | |||
|
427 | 427 | RequestHeader set X-Forwarded-User %{RU}e |
|
428 | 428 | </Location> |
|
429 | 429 | |
|
430 | Setting metadata in container/reverse-proxy | |
|
431 | ''''''''''''''''''''''''''''''''''''''''''' | |
|
432 | ||
|
433 | When a new user account is created on the first login, Kallithea has no information about | |
|
434 | the user's email and full name. So you can set some additional request headers like in the | |
|
435 | example below. In this example the user is authenticated via Kerberos and an Apache | |
|
436 | mod_python fixup handler is used to get the user information from a LDAP server. But you | |
|
437 | could set the request headers however you want. | |
|
438 | ||
|
439 | .. code-block:: apache | |
|
440 | ||
|
441 | <Location /someprefix> | |
|
442 | ProxyPass http://127.0.0.1:5000/someprefix | |
|
443 | ProxyPassReverse http://127.0.0.1:5000/someprefix | |
|
444 | SetEnvIf X-Url-Scheme https HTTPS=1 | |
|
445 | ||
|
446 | AuthName "Kerberos Login" | |
|
447 | AuthType Kerberos | |
|
448 | Krb5Keytab /etc/apache2/http.keytab | |
|
449 | KrbMethodK5Passwd off | |
|
450 | KrbVerifyKDC on | |
|
451 | Require valid-user | |
|
452 | ||
|
453 | PythonFixupHandler ldapmetadata | |
|
454 | ||
|
455 | RequestHeader set X_REMOTE_USER %{X_REMOTE_USER}e | |
|
456 | RequestHeader set X_REMOTE_EMAIL %{X_REMOTE_EMAIL}e | |
|
457 | RequestHeader set X_REMOTE_FIRSTNAME %{X_REMOTE_FIRSTNAME}e | |
|
458 | RequestHeader set X_REMOTE_LASTNAME %{X_REMOTE_LASTNAME}e | |
|
459 | </Location> | |
|
460 | ||
|
461 | .. code-block:: python | |
|
462 | ||
|
463 | from mod_python import apache | |
|
464 | import ldap | |
|
465 | ||
|
466 | LDAP_SERVER = "ldap://server.mydomain.com:389" | |
|
467 | LDAP_USER = "" | |
|
468 | LDAP_PASS = "" | |
|
469 | LDAP_ROOT = "dc=mydomain,dc=com" | |
|
470 | LDAP_FILTER = "sAMAcountName=%s" | |
|
471 | LDAP_ATTR_LIST = ['sAMAcountName','givenname','sn','mail'] | |
|
472 | ||
|
473 | def fixuphandler(req): | |
|
474 | if req.user is None: | |
|
475 | # no user to search for | |
|
476 | return apache.OK | |
|
477 | else: | |
|
478 | try: | |
|
479 | if('\\' in req.user): | |
|
480 | username = req.user.split('\\')[1] | |
|
481 | elif('@' in req.user): | |
|
482 | username = req.user.split('@')[0] | |
|
483 | else: | |
|
484 | username = req.user | |
|
485 | l = ldap.initialize(LDAP_SERVER) | |
|
486 | l.simple_bind_s(LDAP_USER, LDAP_PASS) | |
|
487 | r = l.search_s(LDAP_ROOT, ldap.SCOPE_SUBTREE, LDAP_FILTER % username, attrlist=LDAP_ATTR_LIST) | |
|
488 | ||
|
489 | req.subprocess_env['X_REMOTE_USER'] = username | |
|
490 | req.subprocess_env['X_REMOTE_EMAIL'] = r[0][1]['mail'][0].lower() | |
|
491 | req.subprocess_env['X_REMOTE_FIRSTNAME'] = "%s" % r[0][1]['givenname'][0] | |
|
492 | req.subprocess_env['X_REMOTE_LASTNAME'] = "%s" % r[0][1]['sn'][0] | |
|
493 | except Exception, e: | |
|
494 | apache.log_error("error getting data from ldap %s" % str(e), apache.APLOG_ERR) | |
|
495 | ||
|
496 | return apache.OK | |
|
497 | ||
|
430 | 498 | .. note:: |
|
431 | 499 | If you enable proxy pass-through authentication, make sure your server is |
|
432 | 500 | only accessible through the proxy. Otherwise, any client would be able to |
@@ -29,7 +29,7 b' import logging' | |||
|
29 | 29 | from kallithea.lib import auth_modules |
|
30 | 30 | from kallithea.lib.utils2 import str2bool, safe_unicode |
|
31 | 31 | from kallithea.lib.compat import hybrid_property |
|
32 | from kallithea.model.db import User | |
|
32 | from kallithea.model.db import User, Setting | |
|
33 | 33 | |
|
34 | 34 | log = logging.getLogger(__name__) |
|
35 | 35 | |
@@ -53,15 +53,39 b' class KallitheaAuthPlugin(auth_modules.K' | |||
|
53 | 53 | "name": "header", |
|
54 | 54 | "validator": self.validators.UnicodeString(strip=True, not_empty=True), |
|
55 | 55 | "type": "string", |
|
56 |
"description": " |
|
|
56 | "description": "Request header to extract the username from", | |
|
57 | 57 | "default": "REMOTE_USER", |
|
58 |
"formname": " |
|
|
58 | "formname": "Username header" | |
|
59 | }, | |
|
60 | { | |
|
61 | "name": "email_header", | |
|
62 | "validator": self.validators.UnicodeString(strip=True), | |
|
63 | "type": "string", | |
|
64 | "description": "Optional request header to extract the email from", | |
|
65 | "default": "", | |
|
66 | "formname": "Email header" | |
|
67 | }, | |
|
68 | { | |
|
69 | "name": "firstname_header", | |
|
70 | "validator": self.validators.UnicodeString(strip=True), | |
|
71 | "type": "string", | |
|
72 | "description": "Optional request header to extract the first name from", | |
|
73 | "default": "", | |
|
74 | "formname": "Firstname header" | |
|
75 | }, | |
|
76 | { | |
|
77 | "name": "lastname_header", | |
|
78 | "validator": self.validators.UnicodeString(strip=True), | |
|
79 | "type": "string", | |
|
80 | "description": "Optional request header to extract the last name from", | |
|
81 | "default": "", | |
|
82 | "formname": "Lastname header" | |
|
59 | 83 | }, |
|
60 | 84 | { |
|
61 | 85 | "name": "fallback_header", |
|
62 | 86 | "validator": self.validators.UnicodeString(strip=True), |
|
63 | 87 | "type": "string", |
|
64 |
"description": " |
|
|
88 | "description": "Request header to extract the user from when main one fails", | |
|
65 | 89 | "default": "HTTP_X_FORWARDED_USER", |
|
66 | 90 | "formname": "Fallback header" |
|
67 | 91 | }, |
@@ -172,9 +196,9 b' class KallitheaAuthPlugin(auth_modules.K' | |||
|
172 | 196 | # old attrs fetched from Kallithea database |
|
173 | 197 | admin = getattr(userobj, 'admin', False) |
|
174 | 198 | active = getattr(userobj, 'active', True) |
|
175 | email = getattr(userobj, 'email', '') | |
|
176 | firstname = getattr(userobj, 'firstname', '') | |
|
177 | lastname = getattr(userobj, 'lastname', '') | |
|
199 | email = environ.get(settings.get('email_header'), getattr(userobj, 'email', '')) | |
|
200 | firstname = environ.get(settings.get('firstname_header'), getattr(userobj, 'firstname', '')) | |
|
201 | lastname = environ.get(settings.get('lastname_header'), getattr(userobj, 'lastname', '')) | |
|
178 | 202 | |
|
179 | 203 | user_data = { |
|
180 | 204 | 'username': username, |
@@ -192,4 +216,11 b' class KallitheaAuthPlugin(auth_modules.K' | |||
|
192 | 216 | return user_data |
|
193 | 217 | |
|
194 | 218 | def get_managed_fields(self): |
|
195 |
|
|
|
219 | fields = ['username', 'password'] | |
|
220 | if(Setting.get_by_name('auth_container_email_header').app_settings_value): | |
|
221 | fields.append('email') | |
|
222 | if(Setting.get_by_name('auth_container_firstname_header').app_settings_value): | |
|
223 | fields.append('firstname') | |
|
224 | if(Setting.get_by_name('auth_container_lastname_header').app_settings_value): | |
|
225 | fields.append('lastname') | |
|
226 | return fields |
@@ -135,6 +135,9 b' class TestAuthSettingsController(TestCon' | |||
|
135 | 135 | def test_container_auth_login_header(self): |
|
136 | 136 | self._container_auth_setup( |
|
137 | 137 | auth_container_header='THE_USER_NAME', |
|
138 | auth_container_email_header='', | |
|
139 | auth_container_firstname_header='', | |
|
140 | auth_container_lastname_header='', | |
|
138 | 141 | auth_container_fallback_header='', |
|
139 | 142 | auth_container_clean_username='False', |
|
140 | 143 | ) |
@@ -143,9 +146,34 b' class TestAuthSettingsController(TestCon' | |||
|
143 | 146 | resulting_username='john@example.org', |
|
144 | 147 | ) |
|
145 | 148 | |
|
149 | def test_container_auth_login_header_attr(self): | |
|
150 | self._container_auth_setup( | |
|
151 | auth_container_header='THE_USER_NAME', | |
|
152 | auth_container_email_header='THE_USER_EMAIL', | |
|
153 | auth_container_firstname_header='THE_USER_FIRSTNAME', | |
|
154 | auth_container_lastname_header='THE_USER_LASTNAME', | |
|
155 | auth_container_fallback_header='', | |
|
156 | auth_container_clean_username='False', | |
|
157 | ) | |
|
158 | response = self.app.get( | |
|
159 | url=url(controller='admin/my_account', action='my_account'), | |
|
160 | extra_environ={'THE_USER_NAME': 'johnd', | |
|
161 | 'THE_USER_EMAIL': 'john@example.org', | |
|
162 | 'THE_USER_FIRSTNAME': 'John', | |
|
163 | 'THE_USER_LASTNAME': 'Doe', | |
|
164 | } | |
|
165 | ) | |
|
166 | self.assertEqual(response.form['email'].value, 'john@example.org') | |
|
167 | self.assertEqual(response.form['firstname'].value, 'John') | |
|
168 | self.assertEqual(response.form['lastname'].value, 'Doe') | |
|
169 | ||
|
170 | ||
|
146 | 171 | def test_container_auth_login_fallback_header(self): |
|
147 | 172 | self._container_auth_setup( |
|
148 | 173 | auth_container_header='THE_USER_NAME', |
|
174 | auth_container_email_header='', | |
|
175 | auth_container_firstname_header='', | |
|
176 | auth_container_lastname_header='', | |
|
149 | 177 | auth_container_fallback_header='HTTP_X_YZZY', |
|
150 | 178 | auth_container_clean_username='False', |
|
151 | 179 | ) |
@@ -157,6 +185,9 b' class TestAuthSettingsController(TestCon' | |||
|
157 | 185 | def test_container_auth_clean_username_at(self): |
|
158 | 186 | self._container_auth_setup( |
|
159 | 187 | auth_container_header='REMOTE_USER', |
|
188 | auth_container_email_header='', | |
|
189 | auth_container_firstname_header='', | |
|
190 | auth_container_lastname_header='', | |
|
160 | 191 | auth_container_fallback_header='', |
|
161 | 192 | auth_container_clean_username='True', |
|
162 | 193 | ) |
@@ -168,6 +199,9 b' class TestAuthSettingsController(TestCon' | |||
|
168 | 199 | def test_container_auth_clean_username_backslash(self): |
|
169 | 200 | self._container_auth_setup( |
|
170 | 201 | auth_container_header='REMOTE_USER', |
|
202 | auth_container_email_header='', | |
|
203 | auth_container_firstname_header='', | |
|
204 | auth_container_lastname_header='', | |
|
171 | 205 | auth_container_fallback_header='', |
|
172 | 206 | auth_container_clean_username='True', |
|
173 | 207 | ) |
@@ -179,6 +213,9 b' class TestAuthSettingsController(TestCon' | |||
|
179 | 213 | def test_container_auth_no_logout(self): |
|
180 | 214 | self._container_auth_setup( |
|
181 | 215 | auth_container_header='REMOTE_USER', |
|
216 | auth_container_email_header='', | |
|
217 | auth_container_firstname_header='', | |
|
218 | auth_container_lastname_header='', | |
|
182 | 219 | auth_container_fallback_header='', |
|
183 | 220 | auth_container_clean_username='True', |
|
184 | 221 | ) |
General Comments 0
You need to be logged in to leave comments.
Login now