##// END OF EJS Templates
auth: drop api_access_controllers_whitelist and give API key auth same access as other kinds of auth...
Mads Kiilerich -
r7598:0e3e0864 default
parent child Browse files
Show More
@@ -153,18 +153,6 b' show_revision_number = false'
153 ## Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid>
153 ## Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid>
154 gist_alias_url =
154 gist_alias_url =
155
155
156 ## white list of API enabled controllers. This allows to add list of
157 ## controllers to which access will be enabled by api_key. eg: to enable
158 ## api access to raw_files put `FilesController:raw`, to enable access to patches
159 ## add `ChangesetController:changeset_patch`. This list should be "," separated
160 ## Syntax is <ControllerClass>:<function>. Check debug logs for generated names
161 ## Recommended settings below are commented out:
162 api_access_controllers_whitelist =
163 # ChangesetController:changeset_patch,
164 # ChangesetController:changeset_raw,
165 # FilesController:raw,
166 # FilesController:archivefile
167
168 ## default encoding used to convert from and to unicode
156 ## default encoding used to convert from and to unicode
169 ## can be also a comma separated list of encoding in case of mixed encodings
157 ## can be also a comma separated list of encoding in case of mixed encodings
170 default_encoding = utf-8
158 default_encoding = utf-8
@@ -1238,24 +1238,8 b' OUTPUT::'
1238 API access for web views
1238 API access for web views
1239 ------------------------
1239 ------------------------
1240
1240
1241 API access can also be turned on for each web view in Kallithea that is
1241 Kallithea HTTP entry points can also be accessed without login using bearer
1242 decorated with the ``@LoginRequired`` decorator. Some views use
1242 authentication by including this header with the request::
1243 ``@LoginRequired(api_access=True)`` and are always available. By default only
1244 RSS/Atom feed views are enabled. Other views are
1245 only available if they have been whitelisted. Edit the
1246 ``api_access_controllers_whitelist`` option in your .ini file and define views
1247 that should have API access enabled.
1248
1249 For example, to enable API access to patch/diff, raw file and archive::
1250
1251 api_access_controllers_whitelist =
1252 ChangesetController:changeset_patch,
1253 ChangesetController:changeset_raw,
1254 FilesController:raw,
1255 FilesController:archivefile
1256
1257 After this change, a Kallithea view can be accessed without login using
1258 bearer authentication, by including this header with the request::
1259
1243
1260 Authentication: Bearer <api_key>
1244 Authentication: Bearer <api_key>
1261
1245
@@ -51,7 +51,7 b' ttl = "5"'
51
51
52 class FeedController(BaseRepoController):
52 class FeedController(BaseRepoController):
53
53
54 @LoginRequired(api_access=True, allow_default_user=True)
54 @LoginRequired(allow_default_user=True)
55 @HasRepoPermissionLevelDecorator('read')
55 @HasRepoPermissionLevelDecorator('read')
56 def _before(self, *args, **kwargs):
56 def _before(self, *args, **kwargs):
57 super(FeedController, self)._before(*args, **kwargs)
57 super(FeedController, self)._before(*args, **kwargs)
@@ -220,7 +220,7 b' class JournalController(BaseController):'
220
220
221 return render('journal/journal.html')
221 return render('journal/journal.html')
222
222
223 @LoginRequired(api_access=True)
223 @LoginRequired()
224 def journal_atom(self):
224 def journal_atom(self):
225 """
225 """
226 Produce an atom-1.0 feed via feedgenerator module
226 Produce an atom-1.0 feed via feedgenerator module
@@ -231,7 +231,7 b' class JournalController(BaseController):'
231 .all()
231 .all()
232 return self._atom_feed(following, public=False)
232 return self._atom_feed(following, public=False)
233
233
234 @LoginRequired(api_access=True)
234 @LoginRequired()
235 def journal_rss(self):
235 def journal_rss(self):
236 """
236 """
237 Produce an rss feed via feedgenerator module
237 Produce an rss feed via feedgenerator module
@@ -289,7 +289,7 b' class JournalController(BaseController):'
289
289
290 return render('journal/public_journal.html')
290 return render('journal/public_journal.html')
291
291
292 @LoginRequired(api_access=True, allow_default_user=True)
292 @LoginRequired(allow_default_user=True)
293 def public_journal_atom(self):
293 def public_journal_atom(self):
294 """
294 """
295 Produce an atom-1.0 feed via feedgenerator module
295 Produce an atom-1.0 feed via feedgenerator module
@@ -301,7 +301,7 b' class JournalController(BaseController):'
301
301
302 return self._atom_feed(c.following)
302 return self._atom_feed(c.following)
303
303
304 @LoginRequired(api_access=True, allow_default_user=True)
304 @LoginRequired(allow_default_user=True)
305 def public_journal_rss(self):
305 def public_journal_rss(self):
306 """
306 """
307 Produce an rss2 feed via feedgenerator module
307 Produce an rss2 feed via feedgenerator module
@@ -367,28 +367,6 b' def _cached_perms_data(user_id, user_is_'
367 return permissions
367 return permissions
368
368
369
369
370 def allowed_api_access(controller_name, whitelist=None, api_key=None):
371 """
372 Check if given controller_name is in whitelist API access
373 """
374 if not whitelist:
375 from kallithea import CONFIG
376 whitelist = aslist(CONFIG.get('api_access_controllers_whitelist'),
377 sep=',')
378 log.debug('whitelist of API access is: %s', whitelist)
379 api_access_valid = controller_name in whitelist
380 if api_access_valid:
381 log.debug('controller:%s is in API whitelist', controller_name)
382 else:
383 msg = 'controller: %s is *NOT* in API whitelist' % (controller_name)
384 if api_key:
385 # if we use API key and don't have access it's a warning
386 log.warning(msg)
387 else:
388 log.debug(msg)
389 return api_access_valid
390
391
392 class AuthUser(object):
370 class AuthUser(object):
393 """
371 """
394 Represents a Kallithea user, including various authentication and
372 Represents a Kallithea user, including various authentication and
@@ -689,13 +667,10 b' class LoginRequired(object):'
689 If the "default" user is enabled and allow_default_user is true, that is
667 If the "default" user is enabled and allow_default_user is true, that is
690 considered valid too.
668 considered valid too.
691
669
692 Also checks that IP address is allowed, and if using API key instead
670 Also checks that IP address is allowed.
693 of regular cookie authentication, checks that API key access is allowed
694 (based on `api_access` parameter and the API view whitelist).
695 """
671 """
696
672
697 def __init__(self, api_access=False, allow_default_user=False):
673 def __init__(self, allow_default_user=False):
698 self.api_access = api_access
699 self.allow_default_user = allow_default_user
674 self.allow_default_user = allow_default_user
700
675
701 def __call__(self, func):
676 def __call__(self, func):
@@ -708,16 +683,9 b' class LoginRequired(object):'
708 log.debug('Checking access for user %s @ %s', user, loc)
683 log.debug('Checking access for user %s @ %s', user, loc)
709
684
710 # Check if we used an API key to authenticate.
685 # Check if we used an API key to authenticate.
711 api_key = user.authenticating_api_key
686 if user.authenticating_api_key is not None:
712 if api_key is not None:
713 # Check that controller is enabled for API key usage.
714 if not self.api_access and not allowed_api_access(loc, api_key=api_key):
715 # controller does not allow API access
716 log.warning('API access to %s is not allowed', loc)
717 raise HTTPForbidden()
718
719 log.info('user %s authenticated with API key ****%s @ %s',
687 log.info('user %s authenticated with API key ****%s @ %s',
720 user, api_key[-4:], loc)
688 user, user.authenticating_api_key[-4:], loc)
721 return func(*fargs, **fkwargs)
689 return func(*fargs, **fkwargs)
722
690
723 # CSRF protection: Whenever a request has ambient authority (whether
691 # CSRF protection: Whenever a request has ambient authority (whether
@@ -250,18 +250,6 b' show_revision_number = false'
250 <%text>## Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid></%text>
250 <%text>## Kallithea url, ie. http[s]://kallithea.example.com/_admin/gists/<gistid></%text>
251 gist_alias_url =
251 gist_alias_url =
252
252
253 <%text>## white list of API enabled controllers. This allows to add list of</%text>
254 <%text>## controllers to which access will be enabled by api_key. eg: to enable</%text>
255 <%text>## api access to raw_files put `FilesController:raw`, to enable access to patches</%text>
256 <%text>## add `ChangesetController:changeset_patch`. This list should be "," separated</%text>
257 <%text>## Syntax is <ControllerClass>:<function>. Check debug logs for generated names</%text>
258 <%text>## Recommended settings below are commented out:</%text>
259 api_access_controllers_whitelist =
260 # ChangesetController:changeset_patch,
261 # ChangesetController:changeset_raw,
262 # FilesController:raw,
263 # FilesController:archivefile
264
265 <%text>## default encoding used to convert from and to unicode</%text>
253 <%text>## default encoding used to convert from and to unicode</%text>
266 <%text>## can be also a comma separated list of encoding in case of mixed encodings</%text>
254 <%text>## can be also a comma separated list of encoding in case of mixed encodings</%text>
267 default_encoding = utf-8
255 default_encoding = utf-8
@@ -441,10 +441,6 b' class TestLoginController(TestController'
441 # API
441 # API
442 #==========================================================================
442 #==========================================================================
443
443
444 def _get_api_whitelist(self, values=None):
445 config = {'api_access_controllers_whitelist': values or []}
446 return config
447
448 def _api_key_test(self, api_key, status):
444 def _api_key_test(self, api_key, status):
449 """Verifies HTTP status code for accessing an auth-requiring page,
445 """Verifies HTTP status code for accessing an auth-requiring page,
450 using the given api_key URL parameter as well as using the API key
446 using the given api_key URL parameter as well as using the API key
@@ -476,45 +472,22 b' class TestLoginController(TestController'
476 ('none', None, 302),
472 ('none', None, 302),
477 ('empty_string', '', 403),
473 ('empty_string', '', 403),
478 ('fake_number', '123456', 403),
474 ('fake_number', '123456', 403),
479 ('proper_api_key', True, 403)
480 ])
481 def test_access_not_whitelisted_page_via_api_key(self, test_name, api_key, code):
482 whitelist = self._get_api_whitelist([])
483 with mock.patch('kallithea.CONFIG', whitelist):
484 assert [] == whitelist['api_access_controllers_whitelist']
485 self._api_key_test(api_key, code)
486
487 @parametrize('test_name,api_key,code', [
488 ('none', None, 302),
489 ('empty_string', '', 403),
490 ('fake_number', '123456', 403),
491 ('fake_not_alnum', 'a-z', 403),
475 ('fake_not_alnum', 'a-z', 403),
492 ('fake_api_key', '0123456789abcdef0123456789ABCDEF01234567', 403),
476 ('fake_api_key', '0123456789abcdef0123456789ABCDEF01234567', 403),
493 ('proper_api_key', True, 200)
477 ('proper_api_key', True, 200)
494 ])
478 ])
495 def test_access_whitelisted_page_via_api_key(self, test_name, api_key, code):
479 def test_access_page_via_api_key(self, test_name, api_key, code):
496 whitelist = self._get_api_whitelist(['ChangesetController:changeset_raw'])
480 self._api_key_test(api_key, code)
497 with mock.patch('kallithea.CONFIG', whitelist):
498 assert ['ChangesetController:changeset_raw'] == whitelist['api_access_controllers_whitelist']
499 self._api_key_test(api_key, code)
500
481
501 def test_access_page_via_extra_api_key(self):
482 def test_access_page_via_extra_api_key(self):
502 whitelist = self._get_api_whitelist(['ChangesetController:changeset_raw'])
483 new_api_key = ApiKeyModel().create(TEST_USER_ADMIN_LOGIN, u'test')
503 with mock.patch('kallithea.CONFIG', whitelist):
484 Session().commit()
504 assert ['ChangesetController:changeset_raw'] == whitelist['api_access_controllers_whitelist']
485 self._api_key_test(new_api_key.api_key, status=200)
505
506 new_api_key = ApiKeyModel().create(TEST_USER_ADMIN_LOGIN, u'test')
507 Session().commit()
508 self._api_key_test(new_api_key.api_key, status=200)
509
486
510 def test_access_page_via_expired_api_key(self):
487 def test_access_page_via_expired_api_key(self):
511 whitelist = self._get_api_whitelist(['ChangesetController:changeset_raw'])
488 new_api_key = ApiKeyModel().create(TEST_USER_ADMIN_LOGIN, u'test')
512 with mock.patch('kallithea.CONFIG', whitelist):
489 Session().commit()
513 assert ['ChangesetController:changeset_raw'] == whitelist['api_access_controllers_whitelist']
490 # patch the API key and make it expired
514
491 new_api_key.expires = 0
515 new_api_key = ApiKeyModel().create(TEST_USER_ADMIN_LOGIN, u'test')
492 Session().commit()
516 Session().commit()
493 self._api_key_test(new_api_key.api_key, status=403)
517 # patch the API key and make it expired
518 new_api_key.expires = 0
519 Session().commit()
520 self._api_key_test(new_api_key.api_key, status=403)
General Comments 0
You need to be logged in to leave comments. Login now