##// END OF EJS Templates
auth: let container authentication get email, first and last name from custom headers
domruf -
r5593:ada6571a default
parent child Browse files
Show More
@@ -1,804 +1,872 b''
1 .. _setup:
1 .. _setup:
2
2
3 =====
3 =====
4 Setup
4 Setup
5 =====
5 =====
6
6
7
7
8 Setting up Kallithea
8 Setting up Kallithea
9 --------------------
9 --------------------
10
10
11 First, you will need to create a Kallithea configuration file. Run the
11 First, you will need to create a Kallithea configuration file. Run the
12 following command to do so::
12 following command to do so::
13
13
14 paster make-config Kallithea my.ini
14 paster make-config Kallithea my.ini
15
15
16 This will create the file ``my.ini`` in the current directory. This
16 This will create the file ``my.ini`` in the current directory. This
17 configuration file contains the various settings for Kallithea, e.g.
17 configuration file contains the various settings for Kallithea, e.g.
18 proxy port, email settings, usage of static files, cache, Celery
18 proxy port, email settings, usage of static files, cache, Celery
19 settings, and logging.
19 settings, and logging.
20
20
21 Next, you need to create the databases used by Kallithea. It is recommended to
21 Next, you need to create the databases used by Kallithea. It is recommended to
22 use PostgreSQL or SQLite (default). If you choose a database other than the
22 use PostgreSQL or SQLite (default). If you choose a database other than the
23 default, ensure you properly adjust the database URL in your ``my.ini``
23 default, ensure you properly adjust the database URL in your ``my.ini``
24 configuration file to use this other database. Kallithea currently supports
24 configuration file to use this other database. Kallithea currently supports
25 PostgreSQL, SQLite and MySQL databases. Create the database by running
25 PostgreSQL, SQLite and MySQL databases. Create the database by running
26 the following command::
26 the following command::
27
27
28 paster setup-db my.ini
28 paster setup-db my.ini
29
29
30 This will prompt you for a "root" path. This "root" path is the location where
30 This will prompt you for a "root" path. This "root" path is the location where
31 Kallithea will store all of its repositories on the current machine. After
31 Kallithea will store all of its repositories on the current machine. After
32 entering this "root" path ``setup-db`` will also prompt you for a username
32 entering this "root" path ``setup-db`` will also prompt you for a username
33 and password for the initial admin account which ``setup-db`` sets
33 and password for the initial admin account which ``setup-db`` sets
34 up for you.
34 up for you.
35
35
36 The ``setup-db`` values can also be given on the command line.
36 The ``setup-db`` values can also be given on the command line.
37 Example::
37 Example::
38
38
39 paster setup-db my.ini --user=nn --password=secret --email=nn@example.com --repos=/srv/repos
39 paster setup-db my.ini --user=nn --password=secret --email=nn@example.com --repos=/srv/repos
40
40
41 The ``setup-db`` command will create all needed tables and an
41 The ``setup-db`` command will create all needed tables and an
42 admin account. When choosing a root path you can either use a new
42 admin account. When choosing a root path you can either use a new
43 empty location, or a location which already contains existing
43 empty location, or a location which already contains existing
44 repositories. If you choose a location which contains existing
44 repositories. If you choose a location which contains existing
45 repositories Kallithea will add all of the repositories at the chosen
45 repositories Kallithea will add all of the repositories at the chosen
46 location to its database. (Note: make sure you specify the correct
46 location to its database. (Note: make sure you specify the correct
47 path to the root).
47 path to the root).
48
48
49 .. note:: the given path for Mercurial_ repositories **must** be write
49 .. note:: the given path for Mercurial_ repositories **must** be write
50 accessible for the application. It's very important since
50 accessible for the application. It's very important since
51 the Kallithea web interface will work without write access,
51 the Kallithea web interface will work without write access,
52 but when trying to do a push it will fail with permission
52 but when trying to do a push it will fail with permission
53 denied errors unless it has write access.
53 denied errors unless it has write access.
54
54
55 You are now ready to use Kallithea. To run it simply execute::
55 You are now ready to use Kallithea. To run it simply execute::
56
56
57 paster serve my.ini
57 paster serve my.ini
58
58
59 - This command runs the Kallithea server. The web app should be available at
59 - This command runs the Kallithea server. The web app should be available at
60 http://127.0.0.1:5000. The IP address and port is configurable via the
60 http://127.0.0.1:5000. The IP address and port is configurable via the
61 configuration file created in the previous step.
61 configuration file created in the previous step.
62 - Log in to Kallithea using the admin account created when running ``setup-db``.
62 - Log in to Kallithea using the admin account created when running ``setup-db``.
63 - The default permissions on each repository is read, and the owner is admin.
63 - The default permissions on each repository is read, and the owner is admin.
64 Remember to update these if needed.
64 Remember to update these if needed.
65 - In the admin panel you can toggle LDAP, anonymous, and permissions
65 - In the admin panel you can toggle LDAP, anonymous, and permissions
66 settings, as well as edit more advanced options on users and
66 settings, as well as edit more advanced options on users and
67 repositories.
67 repositories.
68
68
69
69
70 Extensions
70 Extensions
71 ----------
71 ----------
72
72
73 Optionally one can create an ``rcextensions`` package that extends Kallithea
73 Optionally one can create an ``rcextensions`` package that extends Kallithea
74 functionality.
74 functionality.
75 To generate a skeleton extensions package, run::
75 To generate a skeleton extensions package, run::
76
76
77 paster make-rcext my.ini
77 paster make-rcext my.ini
78
78
79 This will create an ``rcextensions`` package next to the specified ``ini`` file.
79 This will create an ``rcextensions`` package next to the specified ``ini`` file.
80 With ``rcextensions`` it's possible to add additional mapping for whoosh,
80 With ``rcextensions`` it's possible to add additional mapping for whoosh,
81 stats and add additional code into the push/pull/create/delete repo hooks,
81 stats and add additional code into the push/pull/create/delete repo hooks,
82 for example for sending signals to build-bots such as Jenkins.
82 for example for sending signals to build-bots such as Jenkins.
83
83
84 See the ``__init__.py`` file inside the generated ``rcextensions`` package
84 See the ``__init__.py`` file inside the generated ``rcextensions`` package
85 for more details.
85 for more details.
86
86
87
87
88 Using Kallithea with SSH
88 Using Kallithea with SSH
89 ------------------------
89 ------------------------
90
90
91 Kallithea currently only hosts repositories using http and https. (The addition
91 Kallithea currently only hosts repositories using http and https. (The addition
92 of ssh hosting is a planned future feature.) However you can easily use ssh in
92 of ssh hosting is a planned future feature.) However you can easily use ssh in
93 parallel with Kallithea. (Repository access via ssh is a standard "out of
93 parallel with Kallithea. (Repository access via ssh is a standard "out of
94 the box" feature of Mercurial_ and you can use this to access any of the
94 the box" feature of Mercurial_ and you can use this to access any of the
95 repositories that Kallithea is hosting. See PublishingRepositories_)
95 repositories that Kallithea is hosting. See PublishingRepositories_)
96
96
97 Kallithea repository structures are kept in directories with the same name
97 Kallithea repository structures are kept in directories with the same name
98 as the project. When using repository groups, each group is a subdirectory.
98 as the project. When using repository groups, each group is a subdirectory.
99 This allows you to easily use ssh for accessing repositories.
99 This allows you to easily use ssh for accessing repositories.
100
100
101 In order to use ssh you need to make sure that your web server and the users'
101 In order to use ssh you need to make sure that your web server and the users'
102 login accounts have the correct permissions set on the appropriate directories.
102 login accounts have the correct permissions set on the appropriate directories.
103
103
104 .. note:: These permissions are independent of any permissions you
104 .. note:: These permissions are independent of any permissions you
105 have set up using the Kallithea web interface.
105 have set up using the Kallithea web interface.
106
106
107 If your main directory (the same as set in Kallithea settings) is for
107 If your main directory (the same as set in Kallithea settings) is for
108 example set to ``/srv/repos`` and the repository you are using is
108 example set to ``/srv/repos`` and the repository you are using is
109 named ``kallithea``, then to clone via ssh you should run::
109 named ``kallithea``, then to clone via ssh you should run::
110
110
111 hg clone ssh://user@kallithea.example.com/srv/repos/kallithea
111 hg clone ssh://user@kallithea.example.com/srv/repos/kallithea
112
112
113 Using other external tools such as mercurial-server_ or using ssh key-based
113 Using other external tools such as mercurial-server_ or using ssh key-based
114 authentication is fully supported.
114 authentication is fully supported.
115
115
116 .. note:: In an advanced setup, in order for your ssh access to use
116 .. note:: In an advanced setup, in order for your ssh access to use
117 the same permissions as set up via the Kallithea web
117 the same permissions as set up via the Kallithea web
118 interface, you can create an authentication hook to connect
118 interface, you can create an authentication hook to connect
119 to the Kallithea db and run check functions for permissions
119 to the Kallithea db and run check functions for permissions
120 against that.
120 against that.
121
121
122
122
123 Setting up Whoosh full text search
123 Setting up Whoosh full text search
124 ----------------------------------
124 ----------------------------------
125
125
126 Kallithea provides full text search of repositories using `Whoosh`__.
126 Kallithea provides full text search of repositories using `Whoosh`__.
127
127
128 .. __: https://pythonhosted.org/Whoosh/
128 .. __: https://pythonhosted.org/Whoosh/
129
129
130 For an incremental index build, run::
130 For an incremental index build, run::
131
131
132 paster make-index my.ini
132 paster make-index my.ini
133
133
134 For a full index rebuild, run::
134 For a full index rebuild, run::
135
135
136 paster make-index my.ini -f
136 paster make-index my.ini -f
137
137
138 The ``--repo-location`` option allows the location of the repositories to be overriden;
138 The ``--repo-location`` option allows the location of the repositories to be overriden;
139 usually, the location is retrieved from the Kallithea database.
139 usually, the location is retrieved from the Kallithea database.
140
140
141 The ``--index-only`` option can be used to limit the indexed repositories to a comma-separated list::
141 The ``--index-only`` option can be used to limit the indexed repositories to a comma-separated list::
142
142
143 paster make-index my.ini --index-only=vcs,kallithea
143 paster make-index my.ini --index-only=vcs,kallithea
144
144
145 To keep your index up-to-date it is necessary to do periodic index builds;
145 To keep your index up-to-date it is necessary to do periodic index builds;
146 for this, it is recommended to use a crontab entry. Example::
146 for this, it is recommended to use a crontab entry. Example::
147
147
148 0 3 * * * /path/to/virtualenv/bin/paster make-index /path/to/kallithea/my.ini
148 0 3 * * * /path/to/virtualenv/bin/paster make-index /path/to/kallithea/my.ini
149
149
150 When using incremental mode (the default), Whoosh will check the last
150 When using incremental mode (the default), Whoosh will check the last
151 modification date of each file and add it to be reindexed if a newer file is
151 modification date of each file and add it to be reindexed if a newer file is
152 available. The indexing daemon checks for any removed files and removes them
152 available. The indexing daemon checks for any removed files and removes them
153 from index.
153 from index.
154
154
155 If you want to rebuild the index from scratch, you can use the ``-f`` flag as above,
155 If you want to rebuild the index from scratch, you can use the ``-f`` flag as above,
156 or in the admin panel you can check the "build from scratch" checkbox.
156 or in the admin panel you can check the "build from scratch" checkbox.
157
157
158
158
159 Setting up LDAP support
159 Setting up LDAP support
160 -----------------------
160 -----------------------
161
161
162 Kallithea supports LDAP authentication. In order
162 Kallithea supports LDAP authentication. In order
163 to use LDAP, you have to install the python-ldap_ package. This package is
163 to use LDAP, you have to install the python-ldap_ package. This package is
164 available via PyPI, so you can install it by running::
164 available via PyPI, so you can install it by running::
165
165
166 pip install python-ldap
166 pip install python-ldap
167
167
168 .. note:: ``python-ldap`` requires some libraries to be installed on
168 .. note:: ``python-ldap`` requires some libraries to be installed on
169 your system, so before installing it check that you have at
169 your system, so before installing it check that you have at
170 least the ``openldap`` and ``sasl`` libraries.
170 least the ``openldap`` and ``sasl`` libraries.
171
171
172 Choose *Admin > Authentication*, click the ``kallithea.lib.auth_modules.auth_ldap`` button
172 Choose *Admin > Authentication*, click the ``kallithea.lib.auth_modules.auth_ldap`` button
173 and then *Save*, to enable the LDAP plugin and configure its settings.
173 and then *Save*, to enable the LDAP plugin and configure its settings.
174
174
175 Here's a typical LDAP setup::
175 Here's a typical LDAP setup::
176
176
177 Connection settings
177 Connection settings
178 Enable LDAP = checked
178 Enable LDAP = checked
179 Host = host.example.com
179 Host = host.example.com
180 Port = 389
180 Port = 389
181 Account = <account>
181 Account = <account>
182 Password = <password>
182 Password = <password>
183 Connection Security = LDAPS connection
183 Connection Security = LDAPS connection
184 Certificate Checks = DEMAND
184 Certificate Checks = DEMAND
185
185
186 Search settings
186 Search settings
187 Base DN = CN=users,DC=host,DC=example,DC=org
187 Base DN = CN=users,DC=host,DC=example,DC=org
188 LDAP Filter = (&(objectClass=user)(!(objectClass=computer)))
188 LDAP Filter = (&(objectClass=user)(!(objectClass=computer)))
189 LDAP Search Scope = SUBTREE
189 LDAP Search Scope = SUBTREE
190
190
191 Attribute mappings
191 Attribute mappings
192 Login Attribute = uid
192 Login Attribute = uid
193 First Name Attribute = firstName
193 First Name Attribute = firstName
194 Last Name Attribute = lastName
194 Last Name Attribute = lastName
195 Email Attribute = mail
195 Email Attribute = mail
196
196
197 If your user groups are placed in an Organisation Unit (OU) structure, the Search Settings configuration differs::
197 If your user groups are placed in an Organisation Unit (OU) structure, the Search Settings configuration differs::
198
198
199 Search settings
199 Search settings
200 Base DN = DC=host,DC=example,DC=org
200 Base DN = DC=host,DC=example,DC=org
201 LDAP Filter = (&(memberOf=CN=your user group,OU=subunit,OU=unit,DC=host,DC=example,DC=org)(objectClass=user))
201 LDAP Filter = (&(memberOf=CN=your user group,OU=subunit,OU=unit,DC=host,DC=example,DC=org)(objectClass=user))
202 LDAP Search Scope = SUBTREE
202 LDAP Search Scope = SUBTREE
203
203
204 .. _enable_ldap:
204 .. _enable_ldap:
205
205
206 Enable LDAP : required
206 Enable LDAP : required
207 Whether to use LDAP for authenticating users.
207 Whether to use LDAP for authenticating users.
208
208
209 .. _ldap_host:
209 .. _ldap_host:
210
210
211 Host : required
211 Host : required
212 LDAP server hostname or IP address. Can be also a comma separated
212 LDAP server hostname or IP address. Can be also a comma separated
213 list of servers to support LDAP fail-over.
213 list of servers to support LDAP fail-over.
214
214
215 .. _Port:
215 .. _Port:
216
216
217 Port : required
217 Port : required
218 389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
218 389 for un-encrypted LDAP, 636 for SSL-encrypted LDAP.
219
219
220 .. _ldap_account:
220 .. _ldap_account:
221
221
222 Account : optional
222 Account : optional
223 Only required if the LDAP server does not allow anonymous browsing of
223 Only required if the LDAP server does not allow anonymous browsing of
224 records. This should be a special account for record browsing. This
224 records. This should be a special account for record browsing. This
225 will require `LDAP Password`_ below.
225 will require `LDAP Password`_ below.
226
226
227 .. _LDAP Password:
227 .. _LDAP Password:
228
228
229 Password : optional
229 Password : optional
230 Only required if the LDAP server does not allow anonymous browsing of
230 Only required if the LDAP server does not allow anonymous browsing of
231 records.
231 records.
232
232
233 .. _Enable LDAPS:
233 .. _Enable LDAPS:
234
234
235 Connection Security : required
235 Connection Security : required
236 Defines the connection to LDAP server
236 Defines the connection to LDAP server
237
237
238 No encryption
238 No encryption
239 Plain non encrypted connection
239 Plain non encrypted connection
240
240
241 LDAPS connection
241 LDAPS connection
242 Enable LDAPS connections. It will likely require `Port`_ to be set to
242 Enable LDAPS connections. It will likely require `Port`_ to be set to
243 a different value (standard LDAPS port is 636). When LDAPS is enabled
243 a different value (standard LDAPS port is 636). When LDAPS is enabled
244 then `Certificate Checks`_ is required.
244 then `Certificate Checks`_ is required.
245
245
246 START_TLS on LDAP connection
246 START_TLS on LDAP connection
247 START TLS connection
247 START TLS connection
248
248
249 .. _Certificate Checks:
249 .. _Certificate Checks:
250
250
251 Certificate Checks : optional
251 Certificate Checks : optional
252 How SSL certificates verification is handled -- this is only useful when
252 How SSL certificates verification is handled -- this is only useful when
253 `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
253 `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security
254 while the other options are susceptible to man-in-the-middle attacks. SSL
254 while the other options are susceptible to man-in-the-middle attacks. SSL
255 certificates can be installed to /etc/openldap/cacerts so that the
255 certificates can be installed to /etc/openldap/cacerts so that the
256 DEMAND or HARD options can be used with self-signed certificates or
256 DEMAND or HARD options can be used with self-signed certificates or
257 certificates that do not have traceable certificates of authority.
257 certificates that do not have traceable certificates of authority.
258
258
259 NEVER
259 NEVER
260 A serve certificate will never be requested or checked.
260 A serve certificate will never be requested or checked.
261
261
262 ALLOW
262 ALLOW
263 A server certificate is requested. Failure to provide a
263 A server certificate is requested. Failure to provide a
264 certificate or providing a bad certificate will not terminate the
264 certificate or providing a bad certificate will not terminate the
265 session.
265 session.
266
266
267 TRY
267 TRY
268 A server certificate is requested. Failure to provide a
268 A server certificate is requested. Failure to provide a
269 certificate does not halt the session; providing a bad certificate
269 certificate does not halt the session; providing a bad certificate
270 halts the session.
270 halts the session.
271
271
272 DEMAND
272 DEMAND
273 A server certificate is requested and must be provided and
273 A server certificate is requested and must be provided and
274 authenticated for the session to proceed.
274 authenticated for the session to proceed.
275
275
276 HARD
276 HARD
277 The same as DEMAND.
277 The same as DEMAND.
278
278
279 .. _Base DN:
279 .. _Base DN:
280
280
281 Base DN : required
281 Base DN : required
282 The Distinguished Name (DN) where searches for users will be performed.
282 The Distinguished Name (DN) where searches for users will be performed.
283 Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
283 Searches can be controlled by `LDAP Filter`_ and `LDAP Search Scope`_.
284
284
285 .. _LDAP Filter:
285 .. _LDAP Filter:
286
286
287 LDAP Filter : optional
287 LDAP Filter : optional
288 A LDAP filter defined by RFC 2254. This is more useful when `LDAP
288 A LDAP filter defined by RFC 2254. This is more useful when `LDAP
289 Search Scope`_ is set to SUBTREE. The filter is useful for limiting
289 Search Scope`_ is set to SUBTREE. The filter is useful for limiting
290 which LDAP objects are identified as representing Users for
290 which LDAP objects are identified as representing Users for
291 authentication. The filter is augmented by `Login Attribute`_ below.
291 authentication. The filter is augmented by `Login Attribute`_ below.
292 This can commonly be left blank.
292 This can commonly be left blank.
293
293
294 .. _LDAP Search Scope:
294 .. _LDAP Search Scope:
295
295
296 LDAP Search Scope : required
296 LDAP Search Scope : required
297 This limits how far LDAP will search for a matching object.
297 This limits how far LDAP will search for a matching object.
298
298
299 BASE
299 BASE
300 Only allows searching of `Base DN`_ and is usually not what you
300 Only allows searching of `Base DN`_ and is usually not what you
301 want.
301 want.
302
302
303 ONELEVEL
303 ONELEVEL
304 Searches all entries under `Base DN`_, but not Base DN itself.
304 Searches all entries under `Base DN`_, but not Base DN itself.
305
305
306 SUBTREE
306 SUBTREE
307 Searches all entries below `Base DN`_, but not Base DN itself.
307 Searches all entries below `Base DN`_, but not Base DN itself.
308 When using SUBTREE `LDAP Filter`_ is useful to limit object
308 When using SUBTREE `LDAP Filter`_ is useful to limit object
309 location.
309 location.
310
310
311 .. _Login Attribute:
311 .. _Login Attribute:
312
312
313 Login Attribute : required
313 Login Attribute : required
314 The LDAP record attribute that will be matched as the USERNAME or
314 The LDAP record attribute that will be matched as the USERNAME or
315 ACCOUNT used to connect to Kallithea. This will be added to `LDAP
315 ACCOUNT used to connect to Kallithea. This will be added to `LDAP
316 Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
316 Filter`_ for locating the User object. If `LDAP Filter`_ is specified as
317 "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
317 "LDAPFILTER", `Login Attribute`_ is specified as "uid" and the user has
318 connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
318 connected as "jsmith" then the `LDAP Filter`_ will be augmented as below
319 ::
319 ::
320
320
321 (&(LDAPFILTER)(uid=jsmith))
321 (&(LDAPFILTER)(uid=jsmith))
322
322
323 .. _ldap_attr_firstname:
323 .. _ldap_attr_firstname:
324
324
325 First Name Attribute : required
325 First Name Attribute : required
326 The LDAP record attribute which represents the user's first name.
326 The LDAP record attribute which represents the user's first name.
327
327
328 .. _ldap_attr_lastname:
328 .. _ldap_attr_lastname:
329
329
330 Last Name Attribute : required
330 Last Name Attribute : required
331 The LDAP record attribute which represents the user's last name.
331 The LDAP record attribute which represents the user's last name.
332
332
333 .. _ldap_attr_email:
333 .. _ldap_attr_email:
334
334
335 Email Attribute : required
335 Email Attribute : required
336 The LDAP record attribute which represents the user's email address.
336 The LDAP record attribute which represents the user's email address.
337
337
338 If all data are entered correctly, and python-ldap_ is properly installed
338 If all data are entered correctly, and python-ldap_ is properly installed
339 users should be granted access to Kallithea with LDAP accounts. At this
339 users should be granted access to Kallithea with LDAP accounts. At this
340 time user information is copied from LDAP into the Kallithea user database.
340 time user information is copied from LDAP into the Kallithea user database.
341 This means that updates of an LDAP user object may not be reflected as a
341 This means that updates of an LDAP user object may not be reflected as a
342 user update in Kallithea.
342 user update in Kallithea.
343
343
344 If You have problems with LDAP access and believe You entered correct
344 If You have problems with LDAP access and believe You entered correct
345 information check out the Kallithea logs, any error messages sent from LDAP
345 information check out the Kallithea logs, any error messages sent from LDAP
346 will be saved there.
346 will be saved there.
347
347
348 Active Directory
348 Active Directory
349 ^^^^^^^^^^^^^^^^
349 ^^^^^^^^^^^^^^^^
350
350
351 Kallithea can use Microsoft Active Directory for user authentication. This
351 Kallithea can use Microsoft Active Directory for user authentication. This
352 is done through an LDAP or LDAPS connection to Active Directory. The
352 is done through an LDAP or LDAPS connection to Active Directory. The
353 following LDAP configuration settings are typical for using Active
353 following LDAP configuration settings are typical for using Active
354 Directory ::
354 Directory ::
355
355
356 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
356 Base DN = OU=SBSUsers,OU=Users,OU=MyBusiness,DC=v3sys,DC=local
357 Login Attribute = sAMAccountName
357 Login Attribute = sAMAccountName
358 First Name Attribute = givenName
358 First Name Attribute = givenName
359 Last Name Attribute = sn
359 Last Name Attribute = sn
360 Email Attribute = mail
360 Email Attribute = mail
361
361
362 All other LDAP settings will likely be site-specific and should be
362 All other LDAP settings will likely be site-specific and should be
363 appropriately configured.
363 appropriately configured.
364
364
365
365
366 Authentication by container or reverse-proxy
366 Authentication by container or reverse-proxy
367 --------------------------------------------
367 --------------------------------------------
368
368
369 Kallithea supports delegating the authentication
369 Kallithea supports delegating the authentication
370 of users to its WSGI container, or to a reverse-proxy server through which all
370 of users to its WSGI container, or to a reverse-proxy server through which all
371 clients access the application.
371 clients access the application.
372
372
373 When these authentication methods are enabled in Kallithea, it uses the
373 When these authentication methods are enabled in Kallithea, it uses the
374 username that the container/proxy (Apache or Nginx, etc.) provides and doesn't
374 username that the container/proxy (Apache or Nginx, etc.) provides and doesn't
375 perform the authentication itself. The authorization, however, is still done by
375 perform the authentication itself. The authorization, however, is still done by
376 Kallithea according to its settings.
376 Kallithea according to its settings.
377
377
378 When a user logs in for the first time using these authentication methods,
378 When a user logs in for the first time using these authentication methods,
379 a matching user account is created in Kallithea with default permissions. An
379 a matching user account is created in Kallithea with default permissions. An
380 administrator can then modify it using Kallithea's admin interface.
380 administrator can then modify it using Kallithea's admin interface.
381
381
382 It's also possible for an administrator to create accounts and configure their
382 It's also possible for an administrator to create accounts and configure their
383 permissions before the user logs in for the first time, using the :ref:`create-user` API.
383 permissions before the user logs in for the first time, using the :ref:`create-user` API.
384
384
385 Container-based authentication
385 Container-based authentication
386 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
386 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
387
387
388 In a container-based authentication setup, Kallithea reads the user name from
388 In a container-based authentication setup, Kallithea reads the user name from
389 the ``REMOTE_USER`` server variable provided by the WSGI container.
389 the ``REMOTE_USER`` server variable provided by the WSGI container.
390
390
391 After setting up your container (see `Apache with mod_wsgi`_), you'll need
391 After setting up your container (see `Apache with mod_wsgi`_), you'll need
392 to configure it to require authentication on the location configured for
392 to configure it to require authentication on the location configured for
393 Kallithea.
393 Kallithea.
394
394
395 Proxy pass-through authentication
395 Proxy pass-through authentication
396 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
396 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
397
397
398 In a proxy pass-through authentication setup, Kallithea reads the user name
398 In a proxy pass-through authentication setup, Kallithea reads the user name
399 from the ``X-Forwarded-User`` request header, which should be configured to be
399 from the ``X-Forwarded-User`` request header, which should be configured to be
400 sent by the reverse-proxy server.
400 sent by the reverse-proxy server.
401
401
402 After setting up your proxy solution (see `Apache virtual host reverse proxy example`_,
402 After setting up your proxy solution (see `Apache virtual host reverse proxy example`_,
403 `Apache as subdirectory`_ or `Nginx virtual host example`_), you'll need to
403 `Apache as subdirectory`_ or `Nginx virtual host example`_), you'll need to
404 configure the authentication and add the username in a request header named
404 configure the authentication and add the username in a request header named
405 ``X-Forwarded-User``.
405 ``X-Forwarded-User``.
406
406
407 For example, the following config section for Apache sets a subdirectory in a
407 For example, the following config section for Apache sets a subdirectory in a
408 reverse-proxy setup with basic auth:
408 reverse-proxy setup with basic auth:
409
409
410 .. code-block:: apache
410 .. code-block:: apache
411
411
412 <Location /someprefix>
412 <Location /someprefix>
413 ProxyPass http://127.0.0.1:5000/someprefix
413 ProxyPass http://127.0.0.1:5000/someprefix
414 ProxyPassReverse http://127.0.0.1:5000/someprefix
414 ProxyPassReverse http://127.0.0.1:5000/someprefix
415 SetEnvIf X-Url-Scheme https HTTPS=1
415 SetEnvIf X-Url-Scheme https HTTPS=1
416
416
417 AuthType Basic
417 AuthType Basic
418 AuthName "Kallithea authentication"
418 AuthName "Kallithea authentication"
419 AuthUserFile /srv/kallithea/.htpasswd
419 AuthUserFile /srv/kallithea/.htpasswd
420 Require valid-user
420 Require valid-user
421
421
422 RequestHeader unset X-Forwarded-User
422 RequestHeader unset X-Forwarded-User
423
423
424 RewriteEngine On
424 RewriteEngine On
425 RewriteCond %{LA-U:REMOTE_USER} (.+)
425 RewriteCond %{LA-U:REMOTE_USER} (.+)
426 RewriteRule .* - [E=RU:%1]
426 RewriteRule .* - [E=RU:%1]
427 RequestHeader set X-Forwarded-User %{RU}e
427 RequestHeader set X-Forwarded-User %{RU}e
428 </Location>
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 .. note::
498 .. note::
431 If you enable proxy pass-through authentication, make sure your server is
499 If you enable proxy pass-through authentication, make sure your server is
432 only accessible through the proxy. Otherwise, any client would be able to
500 only accessible through the proxy. Otherwise, any client would be able to
433 forge the authentication header and could effectively become authenticated
501 forge the authentication header and could effectively become authenticated
434 using any account of their liking.
502 using any account of their liking.
435
503
436
504
437 Integration with issue trackers
505 Integration with issue trackers
438 -------------------------------
506 -------------------------------
439
507
440 Kallithea provides a simple integration with issue trackers. It's possible
508 Kallithea provides a simple integration with issue trackers. It's possible
441 to define a regular expression that will match an issue ID in commit messages,
509 to define a regular expression that will match an issue ID in commit messages,
442 and have that replaced with a URL to the issue. To enable this simply
510 and have that replaced with a URL to the issue. To enable this simply
443 uncomment the following variables in the ini file::
511 uncomment the following variables in the ini file::
444
512
445 issue_pat = (?:^#|\s#)(\w+)
513 issue_pat = (?:^#|\s#)(\w+)
446 issue_server_link = https://issues.example.com/{repo}/issue/{id}
514 issue_server_link = https://issues.example.com/{repo}/issue/{id}
447 issue_prefix = #
515 issue_prefix = #
448
516
449 ``issue_pat`` is the regular expression describing which strings in
517 ``issue_pat`` is the regular expression describing which strings in
450 commit messages will be treated as issue references. A match group in
518 commit messages will be treated as issue references. A match group in
451 parentheses should be used to specify the actual issue id.
519 parentheses should be used to specify the actual issue id.
452
520
453 The default expression matches issues in the format ``#<number>``, e.g., ``#300``.
521 The default expression matches issues in the format ``#<number>``, e.g., ``#300``.
454
522
455 Matched issue references are replaced with the link specified in
523 Matched issue references are replaced with the link specified in
456 ``issue_server_link``. ``{id}`` is replaced with the issue ID, and
524 ``issue_server_link``. ``{id}`` is replaced with the issue ID, and
457 ``{repo}`` with the repository name. Since the # is stripped away,
525 ``{repo}`` with the repository name. Since the # is stripped away,
458 ``issue_prefix`` is prepended to the link text. ``issue_prefix`` doesn't
526 ``issue_prefix`` is prepended to the link text. ``issue_prefix`` doesn't
459 necessarily need to be ``#``: if you set issue prefix to ``ISSUE-`` this will
527 necessarily need to be ``#``: if you set issue prefix to ``ISSUE-`` this will
460 generate a URL in the format:
528 generate a URL in the format:
461
529
462 .. code-block:: html
530 .. code-block:: html
463
531
464 <a href="https://issues.example.com/example_repo/issue/300">ISSUE-300</a>
532 <a href="https://issues.example.com/example_repo/issue/300">ISSUE-300</a>
465
533
466 If needed, more than one pattern can be specified by appending a unique suffix to
534 If needed, more than one pattern can be specified by appending a unique suffix to
467 the variables. For example::
535 the variables. For example::
468
536
469 issue_pat_wiki = (?:wiki-)(.+)
537 issue_pat_wiki = (?:wiki-)(.+)
470 issue_server_link_wiki = https://wiki.example.com/{id}
538 issue_server_link_wiki = https://wiki.example.com/{id}
471 issue_prefix_wiki = WIKI-
539 issue_prefix_wiki = WIKI-
472
540
473 With these settings, wiki pages can be referenced as wiki-some-id, and every
541 With these settings, wiki pages can be referenced as wiki-some-id, and every
474 such reference will be transformed into:
542 such reference will be transformed into:
475
543
476 .. code-block:: html
544 .. code-block:: html
477
545
478 <a href="https://wiki.example.com/some-id">WIKI-some-id</a>
546 <a href="https://wiki.example.com/some-id">WIKI-some-id</a>
479
547
480
548
481 Hook management
549 Hook management
482 ---------------
550 ---------------
483
551
484 Hooks can be managed in similar way to that used in ``.hgrc`` files.
552 Hooks can be managed in similar way to that used in ``.hgrc`` files.
485 To manage hooks, choose *Admin > Settings > Hooks*.
553 To manage hooks, choose *Admin > Settings > Hooks*.
486
554
487 The built-in hooks cannot be modified, though they can be enabled or disabled in the *VCS* section.
555 The built-in hooks cannot be modified, though they can be enabled or disabled in the *VCS* section.
488
556
489 To add another custom hook simply fill in the first textbox with
557 To add another custom hook simply fill in the first textbox with
490 ``<name>.<hook_type>`` and the second with the hook path. Example hooks
558 ``<name>.<hook_type>`` and the second with the hook path. Example hooks
491 can be found in ``kallithea.lib.hooks``.
559 can be found in ``kallithea.lib.hooks``.
492
560
493
561
494 Changing default encoding
562 Changing default encoding
495 -------------------------
563 -------------------------
496
564
497 By default, Kallithea uses UTF-8 encoding.
565 By default, Kallithea uses UTF-8 encoding.
498 This is configurable as ``default_encoding`` in the .ini file.
566 This is configurable as ``default_encoding`` in the .ini file.
499 This affects many parts in Kallithea including user names, filenames, and
567 This affects many parts in Kallithea including user names, filenames, and
500 encoding of commit messages. In addition Kallithea can detect if the ``chardet``
568 encoding of commit messages. In addition Kallithea can detect if the ``chardet``
501 library is installed. If ``chardet`` is detected Kallithea will fallback to it
569 library is installed. If ``chardet`` is detected Kallithea will fallback to it
502 when there are encode/decode errors.
570 when there are encode/decode errors.
503
571
504
572
505 Celery configuration
573 Celery configuration
506 --------------------
574 --------------------
507
575
508 Kallithea can use the distributed task queue system Celery_ to run tasks like
576 Kallithea can use the distributed task queue system Celery_ to run tasks like
509 cloning repositories or sending emails.
577 cloning repositories or sending emails.
510
578
511 Kallithea will in most setups work perfectly fine out of the box (without
579 Kallithea will in most setups work perfectly fine out of the box (without
512 Celery), executing all tasks in the web server process. Some tasks can however
580 Celery), executing all tasks in the web server process. Some tasks can however
513 take some time to run and it can be better to run such tasks asynchronously in
581 take some time to run and it can be better to run such tasks asynchronously in
514 a separate process so the web server can focus on serving web requests.
582 a separate process so the web server can focus on serving web requests.
515
583
516 For installation and configuration of Celery, see the `Celery documentation`_.
584 For installation and configuration of Celery, see the `Celery documentation`_.
517 Note that Celery requires a message broker service like RabbitMQ_ (recommended)
585 Note that Celery requires a message broker service like RabbitMQ_ (recommended)
518 or Redis_.
586 or Redis_.
519
587
520 The use of Celery is configured in the Kallithea ini configuration file.
588 The use of Celery is configured in the Kallithea ini configuration file.
521 To enable it, simply set::
589 To enable it, simply set::
522
590
523 use_celery = true
591 use_celery = true
524
592
525 and add or change the ``celery.*`` and ``broker.*`` configuration variables.
593 and add or change the ``celery.*`` and ``broker.*`` configuration variables.
526
594
527 Remember that the ini files use the format with '.' and not with '_' like
595 Remember that the ini files use the format with '.' and not with '_' like
528 Celery. So for example setting `BROKER_HOST` in Celery means setting
596 Celery. So for example setting `BROKER_HOST` in Celery means setting
529 `broker.host` in the configuration file.
597 `broker.host` in the configuration file.
530
598
531 To start the Celery process, run::
599 To start the Celery process, run::
532
600
533 paster celeryd <configfile.ini>
601 paster celeryd <configfile.ini>
534
602
535 .. note::
603 .. note::
536 Make sure you run this command from the same virtualenv, and with the same
604 Make sure you run this command from the same virtualenv, and with the same
537 user that Kallithea runs.
605 user that Kallithea runs.
538
606
539
607
540 HTTPS support
608 HTTPS support
541 -------------
609 -------------
542
610
543 Kallithea will by default generate URLs based on the WSGI environment.
611 Kallithea will by default generate URLs based on the WSGI environment.
544
612
545 Alternatively, you can use some special configuration settings to control
613 Alternatively, you can use some special configuration settings to control
546 directly which scheme/protocol Kallithea will use when generating URLs:
614 directly which scheme/protocol Kallithea will use when generating URLs:
547
615
548 - With ``https_fixup = true``, the scheme will be taken from the
616 - With ``https_fixup = true``, the scheme will be taken from the
549 ``X-Url-Scheme``, ``X-Forwarded-Scheme`` or ``X-Forwarded-Proto`` HTTP header
617 ``X-Url-Scheme``, ``X-Forwarded-Scheme`` or ``X-Forwarded-Proto`` HTTP header
550 (default ``http``).
618 (default ``http``).
551 - With ``force_https = true`` the default will be ``https``.
619 - With ``force_https = true`` the default will be ``https``.
552 - With ``use_htsts = true``, Kallithea will set ``Strict-Transport-Security`` when using https.
620 - With ``use_htsts = true``, Kallithea will set ``Strict-Transport-Security`` when using https.
553
621
554
622
555 Nginx virtual host example
623 Nginx virtual host example
556 --------------------------
624 --------------------------
557
625
558 Sample config for Nginx using proxy:
626 Sample config for Nginx using proxy:
559
627
560 .. code-block:: nginx
628 .. code-block:: nginx
561
629
562 upstream kallithea {
630 upstream kallithea {
563 server 127.0.0.1:5000;
631 server 127.0.0.1:5000;
564 # add more instances for load balancing
632 # add more instances for load balancing
565 #server 127.0.0.1:5001;
633 #server 127.0.0.1:5001;
566 #server 127.0.0.1:5002;
634 #server 127.0.0.1:5002;
567 }
635 }
568
636
569 ## gist alias
637 ## gist alias
570 server {
638 server {
571 listen 443;
639 listen 443;
572 server_name gist.example.com;
640 server_name gist.example.com;
573 access_log /var/log/nginx/gist.access.log;
641 access_log /var/log/nginx/gist.access.log;
574 error_log /var/log/nginx/gist.error.log;
642 error_log /var/log/nginx/gist.error.log;
575
643
576 ssl on;
644 ssl on;
577 ssl_certificate gist.your.kallithea.server.crt;
645 ssl_certificate gist.your.kallithea.server.crt;
578 ssl_certificate_key gist.your.kallithea.server.key;
646 ssl_certificate_key gist.your.kallithea.server.key;
579
647
580 ssl_session_timeout 5m;
648 ssl_session_timeout 5m;
581
649
582 ssl_protocols SSLv3 TLSv1;
650 ssl_protocols SSLv3 TLSv1;
583 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
651 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
584 ssl_prefer_server_ciphers on;
652 ssl_prefer_server_ciphers on;
585
653
586 rewrite ^/(.+)$ https://kallithea.example.com/_admin/gists/$1;
654 rewrite ^/(.+)$ https://kallithea.example.com/_admin/gists/$1;
587 rewrite (.*) https://kallithea.example.com/_admin/gists;
655 rewrite (.*) https://kallithea.example.com/_admin/gists;
588 }
656 }
589
657
590 server {
658 server {
591 listen 443;
659 listen 443;
592 server_name kallithea.example.com
660 server_name kallithea.example.com
593 access_log /var/log/nginx/kallithea.access.log;
661 access_log /var/log/nginx/kallithea.access.log;
594 error_log /var/log/nginx/kallithea.error.log;
662 error_log /var/log/nginx/kallithea.error.log;
595
663
596 ssl on;
664 ssl on;
597 ssl_certificate your.kallithea.server.crt;
665 ssl_certificate your.kallithea.server.crt;
598 ssl_certificate_key your.kallithea.server.key;
666 ssl_certificate_key your.kallithea.server.key;
599
667
600 ssl_session_timeout 5m;
668 ssl_session_timeout 5m;
601
669
602 ssl_protocols SSLv3 TLSv1;
670 ssl_protocols SSLv3 TLSv1;
603 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
671 ssl_ciphers DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:EDH-RSA-DES-CBC3-SHA:AES256-SHA:DES-CBC3-SHA:AES128-SHA:RC4-SHA:RC4-MD5;
604 ssl_prefer_server_ciphers on;
672 ssl_prefer_server_ciphers on;
605
673
606 ## uncomment root directive if you want to serve static files by nginx
674 ## uncomment root directive if you want to serve static files by nginx
607 ## requires static_files = false in .ini file
675 ## requires static_files = false in .ini file
608 #root /path/to/installation/kallithea/public;
676 #root /path/to/installation/kallithea/public;
609 include /etc/nginx/proxy.conf;
677 include /etc/nginx/proxy.conf;
610 location / {
678 location / {
611 try_files $uri @kallithea;
679 try_files $uri @kallithea;
612 }
680 }
613
681
614 location @kallithea {
682 location @kallithea {
615 proxy_pass http://127.0.0.1:5000;
683 proxy_pass http://127.0.0.1:5000;
616 }
684 }
617
685
618 }
686 }
619
687
620 Here's the proxy.conf. It's tuned so it will not timeout on long
688 Here's the proxy.conf. It's tuned so it will not timeout on long
621 pushes or large pushes::
689 pushes or large pushes::
622
690
623 proxy_redirect off;
691 proxy_redirect off;
624 proxy_set_header Host $host;
692 proxy_set_header Host $host;
625 ## needed for container auth
693 ## needed for container auth
626 #proxy_set_header REMOTE_USER $remote_user;
694 #proxy_set_header REMOTE_USER $remote_user;
627 #proxy_set_header X-Forwarded-User $remote_user;
695 #proxy_set_header X-Forwarded-User $remote_user;
628 proxy_set_header X-Url-Scheme $scheme;
696 proxy_set_header X-Url-Scheme $scheme;
629 proxy_set_header X-Host $http_host;
697 proxy_set_header X-Host $http_host;
630 proxy_set_header X-Real-IP $remote_addr;
698 proxy_set_header X-Real-IP $remote_addr;
631 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
699 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
632 proxy_set_header Proxy-host $proxy_host;
700 proxy_set_header Proxy-host $proxy_host;
633 proxy_buffering off;
701 proxy_buffering off;
634 proxy_connect_timeout 7200;
702 proxy_connect_timeout 7200;
635 proxy_send_timeout 7200;
703 proxy_send_timeout 7200;
636 proxy_read_timeout 7200;
704 proxy_read_timeout 7200;
637 proxy_buffers 8 32k;
705 proxy_buffers 8 32k;
638 client_max_body_size 1024m;
706 client_max_body_size 1024m;
639 client_body_buffer_size 128k;
707 client_body_buffer_size 128k;
640 large_client_header_buffers 8 64k;
708 large_client_header_buffers 8 64k;
641
709
642
710
643 Apache virtual host reverse proxy example
711 Apache virtual host reverse proxy example
644 -----------------------------------------
712 -----------------------------------------
645
713
646 Here is a sample configuration file for Apache using proxy:
714 Here is a sample configuration file for Apache using proxy:
647
715
648 .. code-block:: apache
716 .. code-block:: apache
649
717
650 <VirtualHost *:80>
718 <VirtualHost *:80>
651 ServerName kallithea.example.com
719 ServerName kallithea.example.com
652
720
653 <Proxy *>
721 <Proxy *>
654 # For Apache 2.4 and later:
722 # For Apache 2.4 and later:
655 Require all granted
723 Require all granted
656
724
657 # For Apache 2.2 and earlier, instead use:
725 # For Apache 2.2 and earlier, instead use:
658 # Order allow,deny
726 # Order allow,deny
659 # Allow from all
727 # Allow from all
660 </Proxy>
728 </Proxy>
661
729
662 #important !
730 #important !
663 #Directive to properly generate url (clone url) for pylons
731 #Directive to properly generate url (clone url) for pylons
664 ProxyPreserveHost On
732 ProxyPreserveHost On
665
733
666 #kallithea instance
734 #kallithea instance
667 ProxyPass / http://127.0.0.1:5000/
735 ProxyPass / http://127.0.0.1:5000/
668 ProxyPassReverse / http://127.0.0.1:5000/
736 ProxyPassReverse / http://127.0.0.1:5000/
669
737
670 #to enable https use line below
738 #to enable https use line below
671 #SetEnvIf X-Url-Scheme https HTTPS=1
739 #SetEnvIf X-Url-Scheme https HTTPS=1
672 </VirtualHost>
740 </VirtualHost>
673
741
674 Additional tutorial
742 Additional tutorial
675 http://pylonsbook.com/en/1.1/deployment.html#using-apache-to-proxy-requests-to-pylons
743 http://pylonsbook.com/en/1.1/deployment.html#using-apache-to-proxy-requests-to-pylons
676
744
677
745
678 Apache as subdirectory
746 Apache as subdirectory
679 ----------------------
747 ----------------------
680
748
681 Apache subdirectory part:
749 Apache subdirectory part:
682
750
683 .. code-block:: apache
751 .. code-block:: apache
684
752
685 <Location /<someprefix> >
753 <Location /<someprefix> >
686 ProxyPass http://127.0.0.1:5000/<someprefix>
754 ProxyPass http://127.0.0.1:5000/<someprefix>
687 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
755 ProxyPassReverse http://127.0.0.1:5000/<someprefix>
688 SetEnvIf X-Url-Scheme https HTTPS=1
756 SetEnvIf X-Url-Scheme https HTTPS=1
689 </Location>
757 </Location>
690
758
691 Besides the regular apache setup you will need to add the following line
759 Besides the regular apache setup you will need to add the following line
692 into ``[app:main]`` section of your .ini file::
760 into ``[app:main]`` section of your .ini file::
693
761
694 filter-with = proxy-prefix
762 filter-with = proxy-prefix
695
763
696 Add the following at the end of the .ini file::
764 Add the following at the end of the .ini file::
697
765
698 [filter:proxy-prefix]
766 [filter:proxy-prefix]
699 use = egg:PasteDeploy#prefix
767 use = egg:PasteDeploy#prefix
700 prefix = /<someprefix>
768 prefix = /<someprefix>
701
769
702 then change ``<someprefix>`` into your chosen prefix
770 then change ``<someprefix>`` into your chosen prefix
703
771
704
772
705 Apache with mod_wsgi
773 Apache with mod_wsgi
706 --------------------
774 --------------------
707
775
708 Alternatively, Kallithea can be set up with Apache under mod_wsgi. For
776 Alternatively, Kallithea can be set up with Apache under mod_wsgi. For
709 that, you'll need to:
777 that, you'll need to:
710
778
711 - Install mod_wsgi. If using a Debian-based distro, you can install
779 - Install mod_wsgi. If using a Debian-based distro, you can install
712 the package libapache2-mod-wsgi::
780 the package libapache2-mod-wsgi::
713
781
714 aptitude install libapache2-mod-wsgi
782 aptitude install libapache2-mod-wsgi
715
783
716 - Enable mod_wsgi::
784 - Enable mod_wsgi::
717
785
718 a2enmod wsgi
786 a2enmod wsgi
719
787
720 - Create a wsgi dispatch script, like the one below. Make sure you
788 - Create a wsgi dispatch script, like the one below. Make sure you
721 check that the paths correctly point to where you installed Kallithea
789 check that the paths correctly point to where you installed Kallithea
722 and its Python Virtual Environment.
790 and its Python Virtual Environment.
723 - Enable the ``WSGIScriptAlias`` directive for the WSGI dispatch script,
791 - Enable the ``WSGIScriptAlias`` directive for the WSGI dispatch script,
724 as in the following example. Once again, check the paths are
792 as in the following example. Once again, check the paths are
725 correctly specified.
793 correctly specified.
726
794
727 Here is a sample excerpt from an Apache Virtual Host configuration file:
795 Here is a sample excerpt from an Apache Virtual Host configuration file:
728
796
729 .. code-block:: apache
797 .. code-block:: apache
730
798
731 WSGIDaemonProcess kallithea \
799 WSGIDaemonProcess kallithea \
732 processes=1 threads=4 \
800 processes=1 threads=4 \
733 python-path=/srv/kallithea/venv/lib/python2.7/site-packages
801 python-path=/srv/kallithea/venv/lib/python2.7/site-packages
734 WSGIScriptAlias / /srv/kallithea/dispatch.wsgi
802 WSGIScriptAlias / /srv/kallithea/dispatch.wsgi
735 WSGIPassAuthorization On
803 WSGIPassAuthorization On
736
804
737 Or if using a dispatcher WSGI script with proper virtualenv activation:
805 Or if using a dispatcher WSGI script with proper virtualenv activation:
738
806
739 .. code-block:: apache
807 .. code-block:: apache
740
808
741 WSGIDaemonProcess kallithea processes=1 threads=4
809 WSGIDaemonProcess kallithea processes=1 threads=4
742 WSGIScriptAlias / /srv/kallithea/dispatch.wsgi
810 WSGIScriptAlias / /srv/kallithea/dispatch.wsgi
743 WSGIPassAuthorization On
811 WSGIPassAuthorization On
744
812
745 .. note::
813 .. note::
746 When running apache as root, please make sure it doesn't run Kallithea as
814 When running apache as root, please make sure it doesn't run Kallithea as
747 root, for examply by adding: ``user=www-data group=www-data`` to the configuration.
815 root, for examply by adding: ``user=www-data group=www-data`` to the configuration.
748
816
749 Example WSGI dispatch script:
817 Example WSGI dispatch script:
750
818
751 .. code-block:: python
819 .. code-block:: python
752
820
753 import os
821 import os
754 os.environ["HGENCODING"] = "UTF-8"
822 os.environ["HGENCODING"] = "UTF-8"
755 os.environ['PYTHON_EGG_CACHE'] = '/srv/kallithea/.egg-cache'
823 os.environ['PYTHON_EGG_CACHE'] = '/srv/kallithea/.egg-cache'
756
824
757 # sometimes it's needed to set the curent dir
825 # sometimes it's needed to set the curent dir
758 os.chdir('/srv/kallithea/')
826 os.chdir('/srv/kallithea/')
759
827
760 import site
828 import site
761 site.addsitedir("/srv/kallithea/venv/lib/python2.7/site-packages")
829 site.addsitedir("/srv/kallithea/venv/lib/python2.7/site-packages")
762
830
763 from paste.deploy import loadapp
831 from paste.deploy import loadapp
764 from paste.script.util.logging_config import fileConfig
832 from paste.script.util.logging_config import fileConfig
765
833
766 fileConfig('/srv/kallithea/my.ini')
834 fileConfig('/srv/kallithea/my.ini')
767 application = loadapp('config:/srv/kallithea/my.ini')
835 application = loadapp('config:/srv/kallithea/my.ini')
768
836
769 Or using proper virtualenv activation:
837 Or using proper virtualenv activation:
770
838
771 .. code-block:: python
839 .. code-block:: python
772
840
773 activate_this = '/srv/kallithea/venv/bin/activate_this.py'
841 activate_this = '/srv/kallithea/venv/bin/activate_this.py'
774 execfile(activate_this, dict(__file__=activate_this))
842 execfile(activate_this, dict(__file__=activate_this))
775
843
776 import os
844 import os
777 os.environ['HOME'] = '/srv/kallithea'
845 os.environ['HOME'] = '/srv/kallithea'
778
846
779 ini = '/srv/kallithea/kallithea.ini'
847 ini = '/srv/kallithea/kallithea.ini'
780 from paste.script.util.logging_config import fileConfig
848 from paste.script.util.logging_config import fileConfig
781 fileConfig(ini)
849 fileConfig(ini)
782 from paste.deploy import loadapp
850 from paste.deploy import loadapp
783 application = loadapp('config:' + ini)
851 application = loadapp('config:' + ini)
784
852
785
853
786 Other configuration files
854 Other configuration files
787 -------------------------
855 -------------------------
788
856
789 A number of `example init.d scripts`__ can be found in
857 A number of `example init.d scripts`__ can be found in
790 the ``init.d`` directory of the Kallithea source.
858 the ``init.d`` directory of the Kallithea source.
791
859
792 .. __: https://kallithea-scm.org/repos/kallithea/files/tip/init.d/ .
860 .. __: https://kallithea-scm.org/repos/kallithea/files/tip/init.d/ .
793
861
794
862
795 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
863 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
796 .. _python: http://www.python.org/
864 .. _python: http://www.python.org/
797 .. _Mercurial: http://mercurial.selenic.com/
865 .. _Mercurial: http://mercurial.selenic.com/
798 .. _Celery: http://celeryproject.org/
866 .. _Celery: http://celeryproject.org/
799 .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html
867 .. _Celery documentation: http://docs.celeryproject.org/en/latest/getting-started/index.html
800 .. _RabbitMQ: http://www.rabbitmq.com/
868 .. _RabbitMQ: http://www.rabbitmq.com/
801 .. _Redis: http://redis.io/
869 .. _Redis: http://redis.io/
802 .. _python-ldap: http://www.python-ldap.org/
870 .. _python-ldap: http://www.python-ldap.org/
803 .. _mercurial-server: http://www.lshift.net/mercurial-server.html
871 .. _mercurial-server: http://www.lshift.net/mercurial-server.html
804 .. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
872 .. _PublishingRepositories: http://mercurial.selenic.com/wiki/PublishingRepositories
@@ -1,195 +1,226 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.lib.auth_modules.auth_container
15 kallithea.lib.auth_modules.auth_container
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 Kallithea container based authentication plugin
18 Kallithea container based authentication plugin
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Created on Nov 17, 2012
22 :created_on: Created on Nov 17, 2012
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29 from kallithea.lib import auth_modules
29 from kallithea.lib import auth_modules
30 from kallithea.lib.utils2 import str2bool, safe_unicode
30 from kallithea.lib.utils2 import str2bool, safe_unicode
31 from kallithea.lib.compat import hybrid_property
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 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class KallitheaAuthPlugin(auth_modules.KallitheaExternalAuthPlugin):
37 class KallitheaAuthPlugin(auth_modules.KallitheaExternalAuthPlugin):
38 def __init__(self):
38 def __init__(self):
39 pass
39 pass
40
40
41 @hybrid_property
41 @hybrid_property
42 def name(self):
42 def name(self):
43 return "container"
43 return "container"
44
44
45 @hybrid_property
45 @hybrid_property
46 def is_container_auth(self):
46 def is_container_auth(self):
47 return True
47 return True
48
48
49 def settings(self):
49 def settings(self):
50
50
51 settings = [
51 settings = [
52 {
52 {
53 "name": "header",
53 "name": "header",
54 "validator": self.validators.UnicodeString(strip=True, not_empty=True),
54 "validator": self.validators.UnicodeString(strip=True, not_empty=True),
55 "type": "string",
55 "type": "string",
56 "description": "Header to extract the user from",
56 "description": "Request header to extract the username from",
57 "default": "REMOTE_USER",
57 "default": "REMOTE_USER",
58 "formname": "Header"
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 "name": "fallback_header",
85 "name": "fallback_header",
62 "validator": self.validators.UnicodeString(strip=True),
86 "validator": self.validators.UnicodeString(strip=True),
63 "type": "string",
87 "type": "string",
64 "description": "Header to extract the user from when main one fails",
88 "description": "Request header to extract the user from when main one fails",
65 "default": "HTTP_X_FORWARDED_USER",
89 "default": "HTTP_X_FORWARDED_USER",
66 "formname": "Fallback header"
90 "formname": "Fallback header"
67 },
91 },
68 {
92 {
69 "name": "clean_username",
93 "name": "clean_username",
70 "validator": self.validators.StringBoolean(if_missing=False),
94 "validator": self.validators.StringBoolean(if_missing=False),
71 "type": "bool",
95 "type": "bool",
72 "description": "Perform cleaning of user, if passed user has @ in username "
96 "description": "Perform cleaning of user, if passed user has @ in username "
73 "then first part before @ is taken. "
97 "then first part before @ is taken. "
74 "If there's \\ in the username only the part after \\ is taken",
98 "If there's \\ in the username only the part after \\ is taken",
75 "default": "True",
99 "default": "True",
76 "formname": "Clean username"
100 "formname": "Clean username"
77 },
101 },
78 ]
102 ]
79 return settings
103 return settings
80
104
81 def use_fake_password(self):
105 def use_fake_password(self):
82 return True
106 return True
83
107
84 def user_activation_state(self):
108 def user_activation_state(self):
85 def_user_perms = User.get_default_user().AuthUser.permissions['global']
109 def_user_perms = User.get_default_user().AuthUser.permissions['global']
86 return 'hg.extern_activate.auto' in def_user_perms
110 return 'hg.extern_activate.auto' in def_user_perms
87
111
88 def _clean_username(self, username):
112 def _clean_username(self, username):
89 # Removing realm and domain from username
113 # Removing realm and domain from username
90 username = username.partition('@')[0]
114 username = username.partition('@')[0]
91 username = username.rpartition('\\')[2]
115 username = username.rpartition('\\')[2]
92 return username
116 return username
93
117
94 def _get_username(self, environ, settings):
118 def _get_username(self, environ, settings):
95 username = None
119 username = None
96 environ = environ or {}
120 environ = environ or {}
97 if not environ:
121 if not environ:
98 log.debug('got empty environ: %s', environ)
122 log.debug('got empty environ: %s', environ)
99
123
100 settings = settings or {}
124 settings = settings or {}
101 if settings.get('header'):
125 if settings.get('header'):
102 header = settings.get('header')
126 header = settings.get('header')
103 username = environ.get(header)
127 username = environ.get(header)
104 log.debug('extracted %s:%s', header, username)
128 log.debug('extracted %s:%s', header, username)
105
129
106 # fallback mode
130 # fallback mode
107 if not username and settings.get('fallback_header'):
131 if not username and settings.get('fallback_header'):
108 header = settings.get('fallback_header')
132 header = settings.get('fallback_header')
109 username = environ.get(header)
133 username = environ.get(header)
110 log.debug('extracted %s:%s', header, username)
134 log.debug('extracted %s:%s', header, username)
111
135
112 if username and str2bool(settings.get('clean_username')):
136 if username and str2bool(settings.get('clean_username')):
113 log.debug('Received username %s from container', username)
137 log.debug('Received username %s from container', username)
114 username = self._clean_username(username)
138 username = self._clean_username(username)
115 log.debug('New cleanup user is: %s', username)
139 log.debug('New cleanup user is: %s', username)
116 return username
140 return username
117
141
118 def get_user(self, username=None, **kwargs):
142 def get_user(self, username=None, **kwargs):
119 """
143 """
120 Helper method for user fetching in plugins, by default it's using
144 Helper method for user fetching in plugins, by default it's using
121 simple fetch by username, but this method can be customized in plugins
145 simple fetch by username, but this method can be customized in plugins
122 eg. container auth plugin to fetch user by environ params
146 eg. container auth plugin to fetch user by environ params
123 :param username: username if given to fetch
147 :param username: username if given to fetch
124 :param kwargs: extra arguments needed for user fetching.
148 :param kwargs: extra arguments needed for user fetching.
125 """
149 """
126 environ = kwargs.get('environ') or {}
150 environ = kwargs.get('environ') or {}
127 settings = kwargs.get('settings') or {}
151 settings = kwargs.get('settings') or {}
128 username = self._get_username(environ, settings)
152 username = self._get_username(environ, settings)
129 # we got the username, so use default method now
153 # we got the username, so use default method now
130 return super(KallitheaAuthPlugin, self).get_user(username)
154 return super(KallitheaAuthPlugin, self).get_user(username)
131
155
132 def auth(self, userobj, username, password, settings, **kwargs):
156 def auth(self, userobj, username, password, settings, **kwargs):
133 """
157 """
134 Gets the container_auth username (or email). It tries to get username
158 Gets the container_auth username (or email). It tries to get username
135 from REMOTE_USER if this plugin is enabled, if that fails
159 from REMOTE_USER if this plugin is enabled, if that fails
136 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
160 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
137 is set. clean_username extracts the username from this data if it's
161 is set. clean_username extracts the username from this data if it's
138 having @ in it.
162 having @ in it.
139 Return None on failure. On success, return a dictionary of the form:
163 Return None on failure. On success, return a dictionary of the form:
140
164
141 see: KallitheaAuthPluginBase.auth_func_attrs
165 see: KallitheaAuthPluginBase.auth_func_attrs
142
166
143 :param userobj:
167 :param userobj:
144 :param username:
168 :param username:
145 :param password:
169 :param password:
146 :param settings:
170 :param settings:
147 :param kwargs:
171 :param kwargs:
148 """
172 """
149 environ = kwargs.get('environ')
173 environ = kwargs.get('environ')
150 if not environ:
174 if not environ:
151 log.debug('Empty environ data skipping...')
175 log.debug('Empty environ data skipping...')
152 return None
176 return None
153
177
154 if not userobj:
178 if not userobj:
155 userobj = self.get_user('', environ=environ, settings=settings)
179 userobj = self.get_user('', environ=environ, settings=settings)
156
180
157 # we don't care passed username/password for container auth plugins.
181 # we don't care passed username/password for container auth plugins.
158 # only way to log in is using environ
182 # only way to log in is using environ
159 username = None
183 username = None
160 if userobj:
184 if userobj:
161 username = getattr(userobj, 'username')
185 username = getattr(userobj, 'username')
162
186
163 if not username:
187 if not username:
164 # we don't have any objects in DB, user doesn't exist, extract
188 # we don't have any objects in DB, user doesn't exist, extract
165 # username from environ based on the settings
189 # username from environ based on the settings
166 username = self._get_username(environ, settings)
190 username = self._get_username(environ, settings)
167
191
168 # if cannot fetch username, it's a no-go for this plugin to proceed
192 # if cannot fetch username, it's a no-go for this plugin to proceed
169 if not username:
193 if not username:
170 return None
194 return None
171
195
172 # old attrs fetched from Kallithea database
196 # old attrs fetched from Kallithea database
173 admin = getattr(userobj, 'admin', False)
197 admin = getattr(userobj, 'admin', False)
174 active = getattr(userobj, 'active', True)
198 active = getattr(userobj, 'active', True)
175 email = getattr(userobj, 'email', '')
199 email = environ.get(settings.get('email_header'), getattr(userobj, 'email', ''))
176 firstname = getattr(userobj, 'firstname', '')
200 firstname = environ.get(settings.get('firstname_header'), getattr(userobj, 'firstname', ''))
177 lastname = getattr(userobj, 'lastname', '')
201 lastname = environ.get(settings.get('lastname_header'), getattr(userobj, 'lastname', ''))
178
202
179 user_data = {
203 user_data = {
180 'username': username,
204 'username': username,
181 'firstname': safe_unicode(firstname or username),
205 'firstname': safe_unicode(firstname or username),
182 'lastname': safe_unicode(lastname or ''),
206 'lastname': safe_unicode(lastname or ''),
183 'groups': [],
207 'groups': [],
184 'email': email or '',
208 'email': email or '',
185 'admin': admin or False,
209 'admin': admin or False,
186 'active': active,
210 'active': active,
187 'active_from_extern': True,
211 'active_from_extern': True,
188 'extern_name': username,
212 'extern_name': username,
189 }
213 }
190
214
191 log.info('user `%s` authenticated correctly', user_data['username'])
215 log.info('user `%s` authenticated correctly', user_data['username'])
192 return user_data
216 return user_data
193
217
194 def get_managed_fields(self):
218 def get_managed_fields(self):
195 return ['username', 'password']
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
@@ -1,229 +1,266 b''
1 from kallithea.tests import *
1 from kallithea.tests import *
2 from kallithea.model.db import Setting
2 from kallithea.model.db import Setting
3
3
4
4
5 class TestAuthSettingsController(TestController):
5 class TestAuthSettingsController(TestController):
6 def _enable_plugins(self, plugins_list):
6 def _enable_plugins(self, plugins_list):
7 test_url = url(controller='admin/auth_settings',
7 test_url = url(controller='admin/auth_settings',
8 action='auth_settings')
8 action='auth_settings')
9 params={'auth_plugins': plugins_list, '_authentication_token': self.authentication_token()}
9 params={'auth_plugins': plugins_list, '_authentication_token': self.authentication_token()}
10
10
11 for plugin in plugins_list.split(','):
11 for plugin in plugins_list.split(','):
12 enable = plugin.partition('kallithea.lib.auth_modules.')[-1]
12 enable = plugin.partition('kallithea.lib.auth_modules.')[-1]
13 params.update({'%s_enabled' % enable: True})
13 params.update({'%s_enabled' % enable: True})
14 response = self.app.post(url=test_url, params=params)
14 response = self.app.post(url=test_url, params=params)
15 return params
15 return params
16 #self.checkSessionFlash(response, 'Auth settings updated successfully')
16 #self.checkSessionFlash(response, 'Auth settings updated successfully')
17
17
18 def test_index(self):
18 def test_index(self):
19 self.log_user()
19 self.log_user()
20 response = self.app.get(url(controller='admin/auth_settings',
20 response = self.app.get(url(controller='admin/auth_settings',
21 action='index'))
21 action='index'))
22 response.mustcontain('Authentication Plugins')
22 response.mustcontain('Authentication Plugins')
23
23
24 def test_ldap_save_settings(self):
24 def test_ldap_save_settings(self):
25 self.log_user()
25 self.log_user()
26 if not ldap_lib_installed:
26 if not ldap_lib_installed:
27 raise SkipTest('skipping due to missing ldap lib')
27 raise SkipTest('skipping due to missing ldap lib')
28
28
29 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
29 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
30 params.update({'auth_ldap_host': u'dc.example.com',
30 params.update({'auth_ldap_host': u'dc.example.com',
31 'auth_ldap_port': '999',
31 'auth_ldap_port': '999',
32 'auth_ldap_tls_kind': 'PLAIN',
32 'auth_ldap_tls_kind': 'PLAIN',
33 'auth_ldap_tls_reqcert': 'NEVER',
33 'auth_ldap_tls_reqcert': 'NEVER',
34 'auth_ldap_dn_user': 'test_user',
34 'auth_ldap_dn_user': 'test_user',
35 'auth_ldap_dn_pass': 'test_pass',
35 'auth_ldap_dn_pass': 'test_pass',
36 'auth_ldap_base_dn': 'test_base_dn',
36 'auth_ldap_base_dn': 'test_base_dn',
37 'auth_ldap_filter': 'test_filter',
37 'auth_ldap_filter': 'test_filter',
38 'auth_ldap_search_scope': 'BASE',
38 'auth_ldap_search_scope': 'BASE',
39 'auth_ldap_attr_login': 'test_attr_login',
39 'auth_ldap_attr_login': 'test_attr_login',
40 'auth_ldap_attr_firstname': 'ima',
40 'auth_ldap_attr_firstname': 'ima',
41 'auth_ldap_attr_lastname': 'tester',
41 'auth_ldap_attr_lastname': 'tester',
42 'auth_ldap_attr_email': 'test@example.com'})
42 'auth_ldap_attr_email': 'test@example.com'})
43
43
44 test_url = url(controller='admin/auth_settings',
44 test_url = url(controller='admin/auth_settings',
45 action='auth_settings')
45 action='auth_settings')
46
46
47 response = self.app.post(url=test_url, params=params)
47 response = self.app.post(url=test_url, params=params)
48 self.checkSessionFlash(response, 'Auth settings updated successfully')
48 self.checkSessionFlash(response, 'Auth settings updated successfully')
49
49
50 new_settings = Setting.get_auth_settings()
50 new_settings = Setting.get_auth_settings()
51 self.assertEqual(new_settings['auth_ldap_host'], u'dc.example.com',
51 self.assertEqual(new_settings['auth_ldap_host'], u'dc.example.com',
52 'fail db write compare')
52 'fail db write compare')
53
53
54 def test_ldap_error_form_wrong_port_number(self):
54 def test_ldap_error_form_wrong_port_number(self):
55 self.log_user()
55 self.log_user()
56 if not ldap_lib_installed:
56 if not ldap_lib_installed:
57 raise SkipTest('skipping due to missing ldap lib')
57 raise SkipTest('skipping due to missing ldap lib')
58
58
59 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
59 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
60 params.update({'auth_ldap_host': '',
60 params.update({'auth_ldap_host': '',
61 'auth_ldap_port': 'i-should-be-number', # bad port num
61 'auth_ldap_port': 'i-should-be-number', # bad port num
62 'auth_ldap_tls_kind': 'PLAIN',
62 'auth_ldap_tls_kind': 'PLAIN',
63 'auth_ldap_tls_reqcert': 'NEVER',
63 'auth_ldap_tls_reqcert': 'NEVER',
64 'auth_ldap_dn_user': '',
64 'auth_ldap_dn_user': '',
65 'auth_ldap_dn_pass': '',
65 'auth_ldap_dn_pass': '',
66 'auth_ldap_base_dn': '',
66 'auth_ldap_base_dn': '',
67 'auth_ldap_filter': '',
67 'auth_ldap_filter': '',
68 'auth_ldap_search_scope': 'BASE',
68 'auth_ldap_search_scope': 'BASE',
69 'auth_ldap_attr_login': '',
69 'auth_ldap_attr_login': '',
70 'auth_ldap_attr_firstname': '',
70 'auth_ldap_attr_firstname': '',
71 'auth_ldap_attr_lastname': '',
71 'auth_ldap_attr_lastname': '',
72 'auth_ldap_attr_email': ''})
72 'auth_ldap_attr_email': ''})
73 test_url = url(controller='admin/auth_settings',
73 test_url = url(controller='admin/auth_settings',
74 action='auth_settings')
74 action='auth_settings')
75
75
76 response = self.app.post(url=test_url, params=params)
76 response = self.app.post(url=test_url, params=params)
77
77
78 response.mustcontain("""<span class="error-message">"""
78 response.mustcontain("""<span class="error-message">"""
79 """Please enter a number</span>""")
79 """Please enter a number</span>""")
80
80
81 def test_ldap_error_form(self):
81 def test_ldap_error_form(self):
82 self.log_user()
82 self.log_user()
83 if not ldap_lib_installed:
83 if not ldap_lib_installed:
84 raise SkipTest('skipping due to missing ldap lib')
84 raise SkipTest('skipping due to missing ldap lib')
85
85
86 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
86 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_ldap')
87 params.update({'auth_ldap_host': 'Host',
87 params.update({'auth_ldap_host': 'Host',
88 'auth_ldap_port': '123',
88 'auth_ldap_port': '123',
89 'auth_ldap_tls_kind': 'PLAIN',
89 'auth_ldap_tls_kind': 'PLAIN',
90 'auth_ldap_tls_reqcert': 'NEVER',
90 'auth_ldap_tls_reqcert': 'NEVER',
91 'auth_ldap_dn_user': '',
91 'auth_ldap_dn_user': '',
92 'auth_ldap_dn_pass': '',
92 'auth_ldap_dn_pass': '',
93 'auth_ldap_base_dn': '',
93 'auth_ldap_base_dn': '',
94 'auth_ldap_filter': '',
94 'auth_ldap_filter': '',
95 'auth_ldap_search_scope': 'BASE',
95 'auth_ldap_search_scope': 'BASE',
96 'auth_ldap_attr_login': '', # <----- missing required input
96 'auth_ldap_attr_login': '', # <----- missing required input
97 'auth_ldap_attr_firstname': '',
97 'auth_ldap_attr_firstname': '',
98 'auth_ldap_attr_lastname': '',
98 'auth_ldap_attr_lastname': '',
99 'auth_ldap_attr_email': ''})
99 'auth_ldap_attr_email': ''})
100
100
101 test_url = url(controller='admin/auth_settings',
101 test_url = url(controller='admin/auth_settings',
102 action='auth_settings')
102 action='auth_settings')
103
103
104 response = self.app.post(url=test_url, params=params)
104 response = self.app.post(url=test_url, params=params)
105
105
106 response.mustcontain("""<span class="error-message">The LDAP Login"""
106 response.mustcontain("""<span class="error-message">The LDAP Login"""
107 """ attribute of the CN must be specified""")
107 """ attribute of the CN must be specified""")
108
108
109 def test_ldap_login(self):
109 def test_ldap_login(self):
110 pass
110 pass
111
111
112 def test_ldap_login_incorrect(self):
112 def test_ldap_login_incorrect(self):
113 pass
113 pass
114
114
115 def _container_auth_setup(self, **settings):
115 def _container_auth_setup(self, **settings):
116 self.log_user()
116 self.log_user()
117
117
118 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_container')
118 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_container')
119 params.update(settings)
119 params.update(settings)
120
120
121 test_url = url(controller='admin/auth_settings',
121 test_url = url(controller='admin/auth_settings',
122 action='auth_settings')
122 action='auth_settings')
123
123
124 response = self.app.post(url=test_url, params=params)
124 response = self.app.post(url=test_url, params=params)
125 response = response.follow()
125 response = response.follow()
126 response.click('Log Out') # end admin login session
126 response.click('Log Out') # end admin login session
127
127
128 def _container_auth_verify_login(self, resulting_username, **get_kwargs):
128 def _container_auth_verify_login(self, resulting_username, **get_kwargs):
129 response = self.app.get(
129 response = self.app.get(
130 url=url(controller='admin/my_account', action='my_account'),
130 url=url(controller='admin/my_account', action='my_account'),
131 **get_kwargs
131 **get_kwargs
132 )
132 )
133 response.mustcontain('My Account %s' % resulting_username)
133 response.mustcontain('My Account %s' % resulting_username)
134
134
135 def test_container_auth_login_header(self):
135 def test_container_auth_login_header(self):
136 self._container_auth_setup(
136 self._container_auth_setup(
137 auth_container_header='THE_USER_NAME',
137 auth_container_header='THE_USER_NAME',
138 auth_container_email_header='',
139 auth_container_firstname_header='',
140 auth_container_lastname_header='',
138 auth_container_fallback_header='',
141 auth_container_fallback_header='',
139 auth_container_clean_username='False',
142 auth_container_clean_username='False',
140 )
143 )
141 self._container_auth_verify_login(
144 self._container_auth_verify_login(
142 extra_environ={'THE_USER_NAME': 'john@example.org'},
145 extra_environ={'THE_USER_NAME': 'john@example.org'},
143 resulting_username='john@example.org',
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 def test_container_auth_login_fallback_header(self):
171 def test_container_auth_login_fallback_header(self):
147 self._container_auth_setup(
172 self._container_auth_setup(
148 auth_container_header='THE_USER_NAME',
173 auth_container_header='THE_USER_NAME',
174 auth_container_email_header='',
175 auth_container_firstname_header='',
176 auth_container_lastname_header='',
149 auth_container_fallback_header='HTTP_X_YZZY',
177 auth_container_fallback_header='HTTP_X_YZZY',
150 auth_container_clean_username='False',
178 auth_container_clean_username='False',
151 )
179 )
152 self._container_auth_verify_login(
180 self._container_auth_verify_login(
153 headers={'X-Yzzy': r'foo\bar'},
181 headers={'X-Yzzy': r'foo\bar'},
154 resulting_username=r'foo\bar',
182 resulting_username=r'foo\bar',
155 )
183 )
156
184
157 def test_container_auth_clean_username_at(self):
185 def test_container_auth_clean_username_at(self):
158 self._container_auth_setup(
186 self._container_auth_setup(
159 auth_container_header='REMOTE_USER',
187 auth_container_header='REMOTE_USER',
188 auth_container_email_header='',
189 auth_container_firstname_header='',
190 auth_container_lastname_header='',
160 auth_container_fallback_header='',
191 auth_container_fallback_header='',
161 auth_container_clean_username='True',
192 auth_container_clean_username='True',
162 )
193 )
163 self._container_auth_verify_login(
194 self._container_auth_verify_login(
164 extra_environ={'REMOTE_USER': 'john@example.org'},
195 extra_environ={'REMOTE_USER': 'john@example.org'},
165 resulting_username='john',
196 resulting_username='john',
166 )
197 )
167
198
168 def test_container_auth_clean_username_backslash(self):
199 def test_container_auth_clean_username_backslash(self):
169 self._container_auth_setup(
200 self._container_auth_setup(
170 auth_container_header='REMOTE_USER',
201 auth_container_header='REMOTE_USER',
202 auth_container_email_header='',
203 auth_container_firstname_header='',
204 auth_container_lastname_header='',
171 auth_container_fallback_header='',
205 auth_container_fallback_header='',
172 auth_container_clean_username='True',
206 auth_container_clean_username='True',
173 )
207 )
174 self._container_auth_verify_login(
208 self._container_auth_verify_login(
175 extra_environ={'REMOTE_USER': r'example\jane'},
209 extra_environ={'REMOTE_USER': r'example\jane'},
176 resulting_username=r'jane',
210 resulting_username=r'jane',
177 )
211 )
178
212
179 def test_container_auth_no_logout(self):
213 def test_container_auth_no_logout(self):
180 self._container_auth_setup(
214 self._container_auth_setup(
181 auth_container_header='REMOTE_USER',
215 auth_container_header='REMOTE_USER',
216 auth_container_email_header='',
217 auth_container_firstname_header='',
218 auth_container_lastname_header='',
182 auth_container_fallback_header='',
219 auth_container_fallback_header='',
183 auth_container_clean_username='True',
220 auth_container_clean_username='True',
184 )
221 )
185 response = self.app.get(
222 response = self.app.get(
186 url=url(controller='admin/my_account', action='my_account'),
223 url=url(controller='admin/my_account', action='my_account'),
187 extra_environ={'REMOTE_USER': 'john'},
224 extra_environ={'REMOTE_USER': 'john'},
188 )
225 )
189 self.assertNotIn('Log Out', response.normal_body)
226 self.assertNotIn('Log Out', response.normal_body)
190
227
191 def test_crowd_save_settings(self):
228 def test_crowd_save_settings(self):
192 self.log_user()
229 self.log_user()
193
230
194 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_crowd')
231 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_crowd')
195 params.update({'auth_crowd_host': ' hostname ',
232 params.update({'auth_crowd_host': ' hostname ',
196 'auth_crowd_app_password': 'secret',
233 'auth_crowd_app_password': 'secret',
197 'auth_crowd_admin_groups': 'mygroup',
234 'auth_crowd_admin_groups': 'mygroup',
198 'auth_crowd_port': '123',
235 'auth_crowd_port': '123',
199 'auth_crowd_app_name': 'xyzzy'})
236 'auth_crowd_app_name': 'xyzzy'})
200
237
201 test_url = url(controller='admin/auth_settings',
238 test_url = url(controller='admin/auth_settings',
202 action='auth_settings')
239 action='auth_settings')
203
240
204 response = self.app.post(url=test_url, params=params)
241 response = self.app.post(url=test_url, params=params)
205 self.checkSessionFlash(response, 'Auth settings updated successfully')
242 self.checkSessionFlash(response, 'Auth settings updated successfully')
206
243
207 new_settings = Setting.get_auth_settings()
244 new_settings = Setting.get_auth_settings()
208 self.assertEqual(new_settings['auth_crowd_host'], u'hostname',
245 self.assertEqual(new_settings['auth_crowd_host'], u'hostname',
209 'fail db write compare')
246 'fail db write compare')
210
247
211 def test_pam_save_settings(self):
248 def test_pam_save_settings(self):
212 self.log_user()
249 self.log_user()
213
250
214 if not pam_lib_installed:
251 if not pam_lib_installed:
215 raise SkipTest('skipping due to missing pam lib')
252 raise SkipTest('skipping due to missing pam lib')
216
253
217 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_pam')
254 params = self._enable_plugins('kallithea.lib.auth_modules.auth_internal,kallithea.lib.auth_modules.auth_pam')
218 params.update({'auth_pam_service': 'kallithea',
255 params.update({'auth_pam_service': 'kallithea',
219 'auth_pam_gecos': '^foo-.*'})
256 'auth_pam_gecos': '^foo-.*'})
220
257
221 test_url = url(controller='admin/auth_settings',
258 test_url = url(controller='admin/auth_settings',
222 action='auth_settings')
259 action='auth_settings')
223
260
224 response = self.app.post(url=test_url, params=params)
261 response = self.app.post(url=test_url, params=params)
225 self.checkSessionFlash(response, 'Auth settings updated successfully')
262 self.checkSessionFlash(response, 'Auth settings updated successfully')
226
263
227 new_settings = Setting.get_auth_settings()
264 new_settings = Setting.get_auth_settings()
228 self.assertEqual(new_settings['auth_pam_service'], u'kallithea',
265 self.assertEqual(new_settings['auth_pam_service'], u'kallithea',
229 'fail db write compare')
266 'fail db write compare')
General Comments 0
You need to be logged in to leave comments. Login now