##// END OF EJS Templates
authn: Add an INI option to set an authentication plugin fallback. #3953...
johbo -
r52:a007b8c5 default
parent child Browse files
Show More
@@ -56,7 +56,7 b' def includeme(config):'
56 config.set_authentication_policy(authn_policy)
56 config.set_authentication_policy(authn_policy)
57
57
58 # Create authentication plugin registry and add it to the pyramid registry.
58 # Create authentication plugin registry and add it to the pyramid registry.
59 authn_registry = AuthenticationPluginRegistry()
59 authn_registry = AuthenticationPluginRegistry(config.get_settings())
60 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
60 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
61 config.registry.registerUtility(authn_registry)
61 config.registry.registerUtility(authn_registry)
62
62
@@ -26,23 +26,16 b' import logging'
26 import time
26 import time
27 import traceback
27 import traceback
28
28
29 from authomatic import Authomatic
30 from authomatic.adapters import WebObAdapter
31 from authomatic.providers import oauth2, oauth1
32 from pylons import url
33 from pylons.controllers.util import Response
34 from pylons.i18n.translation import _
35 from pyramid.threadlocal import get_current_registry
29 from pyramid.threadlocal import get_current_registry
36 from sqlalchemy.ext.hybrid import hybrid_property
30 from sqlalchemy.ext.hybrid import hybrid_property
37
31
38 import rhodecode.lib.helpers as h
39 from rhodecode.authentication.interface import IAuthnPluginRegistry
32 from rhodecode.authentication.interface import IAuthnPluginRegistry
40 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
33 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
41 from rhodecode.lib import caches
34 from rhodecode.lib import caches
42 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
35 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
43 from rhodecode.lib.utils2 import md5_safe, safe_int
36 from rhodecode.lib.utils2 import md5_safe, safe_int
44 from rhodecode.lib.utils2 import safe_str
37 from rhodecode.lib.utils2 import safe_str
45 from rhodecode.model.db import User, ExternalIdentity
38 from rhodecode.model.db import User
46 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
47 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.settings import SettingsModel
48 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
@@ -518,50 +511,40 b' def authenticate(username, password, env'
518 raise ValueError('auth type must be on of http, vcs got "%s" instead'
511 raise ValueError('auth type must be on of http, vcs got "%s" instead'
519 % auth_type)
512 % auth_type)
520 container_only = environ and not (username and password)
513 container_only = environ and not (username and password)
521 auth_plugins = SettingsModel().get_auth_plugins()
522 for plugin_id in auth_plugins:
523 plugin = loadplugin(plugin_id)
524
514
525 if plugin is None:
515 authn_registry = get_current_registry().getUtility(IAuthnPluginRegistry)
526 log.warning('Authentication plugin missing: "{}"'.format(
516 for plugin in authn_registry.get_plugins_for_authentication():
527 plugin_id))
528 continue
529
530 if not plugin.is_active():
531 log.info('Authentication plugin is inactive: "{}"'.format(
532 plugin_id))
533 continue
534
535 plugin.set_auth_type(auth_type)
517 plugin.set_auth_type(auth_type)
536 user = plugin.get_user(username)
518 user = plugin.get_user(username)
537 display_user = user.username if user else username
519 display_user = user.username if user else username
538
520
539 if container_only and not plugin.is_container_auth:
521 if container_only and not plugin.is_container_auth:
540 log.debug('Auth type is for container only and plugin `%s` is not '
522 log.debug('Auth type is for container only and plugin `%s` is not '
541 'container plugin, skipping...', plugin_id)
523 'container plugin, skipping...', plugin.get_id())
542 continue
524 continue
543
525
544 # load plugin settings from RhodeCode database
526 # load plugin settings from RhodeCode database
545 plugin_settings = plugin.get_settings()
527 plugin_settings = plugin.get_settings()
546 log.debug('Plugin settings:%s', plugin_settings)
528 log.debug('Plugin settings:%s', plugin_settings)
547
529
548 log.debug('Trying authentication using ** %s **', plugin_id)
530 log.debug('Trying authentication using ** %s **', plugin.get_id())
549 # use plugin's method of user extraction.
531 # use plugin's method of user extraction.
550 user = plugin.get_user(username, environ=environ,
532 user = plugin.get_user(username, environ=environ,
551 settings=plugin_settings)
533 settings=plugin_settings)
552 display_user = user.username if user else username
534 display_user = user.username if user else username
553 log.debug('Plugin %s extracted user is `%s`', plugin_id, display_user)
535 log.debug(
536 'Plugin %s extracted user is `%s`', plugin.get_id(), display_user)
554
537
555 if not plugin.allows_authentication_from(user):
538 if not plugin.allows_authentication_from(user):
556 log.debug('Plugin %s does not accept user `%s` for authentication',
539 log.debug('Plugin %s does not accept user `%s` for authentication',
557 plugin_id, display_user)
540 plugin.get_id(), display_user)
558 continue
541 continue
559 else:
542 else:
560 log.debug('Plugin %s accepted user `%s` for authentication',
543 log.debug('Plugin %s accepted user `%s` for authentication',
561 plugin_id, display_user)
544 plugin.get_id(), display_user)
562
545
563 log.info('Authenticating user `%s` using %s plugin',
546 log.info('Authenticating user `%s` using %s plugin',
564 display_user, plugin_id)
547 display_user, plugin.get_id())
565
548
566 _cache_ttl = 0
549 _cache_ttl = 0
567
550
@@ -576,7 +559,7 b' def authenticate(username, password, env'
576 # get instance of cache manager configured for a namespace
559 # get instance of cache manager configured for a namespace
577 cache_manager = get_auth_cache_manager(custom_ttl=_cache_ttl)
560 cache_manager = get_auth_cache_manager(custom_ttl=_cache_ttl)
578
561
579 log.debug('Cache for plugin `%s` active: %s', plugin_id,
562 log.debug('Cache for plugin `%s` active: %s', plugin.get_id(),
580 plugin_cache_active)
563 plugin_cache_active)
581
564
582 # for environ based password can be empty, but then the validation is
565 # for environ based password can be empty, but then the validation is
@@ -591,7 +574,7 b' def authenticate(username, password, env'
591 # then auth is correct.
574 # then auth is correct.
592 start = time.time()
575 start = time.time()
593 log.debug('Running plugin `%s` _authenticate method',
576 log.debug('Running plugin `%s` _authenticate method',
594 plugin_id)
577 plugin.get_id())
595
578
596 def auth_func():
579 def auth_func():
597 """
580 """
@@ -611,7 +594,7 b' def authenticate(username, password, env'
611 auth_time = time.time() - start
594 auth_time = time.time() - start
612 log.debug('Authentication for plugin `%s` completed in %.3fs, '
595 log.debug('Authentication for plugin `%s` completed in %.3fs, '
613 'expiration time of fetched cache %.1fs.',
596 'expiration time of fetched cache %.1fs.',
614 plugin_id, auth_time, _cache_ttl)
597 plugin.get_id(), auth_time, _cache_ttl)
615
598
616 log.debug('PLUGIN USER DATA: %s', plugin_user)
599 log.debug('PLUGIN USER DATA: %s', plugin_user)
617
600
@@ -620,5 +603,5 b' def authenticate(username, password, env'
620 return plugin_user
603 return plugin_user
621 # we failed to Auth because .auth() method didn't return proper user
604 # we failed to Auth because .auth() method didn't return proper user
622 log.debug("User `%s` failed to authenticate against %s",
605 log.debug("User `%s` failed to authenticate against %s",
623 display_user, plugin_id)
606 display_user, plugin.get_id())
624 return None
607 return None
@@ -31,8 +31,13 b' log = logging.getLogger(__name__)'
31
31
32 @implementer(IAuthnPluginRegistry)
32 @implementer(IAuthnPluginRegistry)
33 class AuthenticationPluginRegistry(object):
33 class AuthenticationPluginRegistry(object):
34 def __init__(self):
34
35 # INI settings key to set a fallback authentication plugin.
36 fallback_plugin_key = 'rhodecode.auth_plugin_fallback'
37
38 def __init__(self, settings):
35 self._plugins = {}
39 self._plugins = {}
40 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
36
41
37 def add_authn_plugin(self, config, plugin):
42 def add_authn_plugin(self, config, plugin):
38 plugin_id = plugin.get_id()
43 plugin_id = plugin.get_id()
@@ -51,3 +56,23 b' class AuthenticationPluginRegistry(objec'
51
56
52 def get_plugin(self, plugin_id):
57 def get_plugin(self, plugin_id):
53 return self._plugins.get(plugin_id, None)
58 return self._plugins.get(plugin_id, None)
59
60 def get_plugins_for_authentication(self):
61 """
62 Returns a list of plugins which should be consulted when authenticating
63 a user. It only returns plugins which are enabled and active.
64 Additionally it includes the fallback plugin from the INI file, if
65 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
66 """
67 plugins = []
68 for plugin in self.get_plugins():
69 if (self._fallback_plugin and
70 plugin.get_id() == self._fallback_plugin):
71 log.warn(
72 'Using fallback authentication plugin from INI file: "%s"',
73 plugin.get_id())
74 plugins.append(plugin)
75 elif plugin.is_enabled() and plugin.is_active():
76 plugins.append(plugin)
77
78 return plugins
@@ -151,8 +151,7 b' class AuthSettingsView(object):'
151 @HasPermissionAllDecorator('hg.admin')
151 @HasPermissionAllDecorator('hg.admin')
152 def index(self, defaults={}, errors=None, prefix_error=False):
152 def index(self, defaults={}, errors=None, prefix_error=False):
153 authn_registry = self.request.registry.getUtility(IAuthnPluginRegistry)
153 authn_registry = self.request.registry.getUtility(IAuthnPluginRegistry)
154 default_plugins = ['egg:rhodecode-enterprise-ce#rhodecode']
154 enabled_plugins = SettingsModel().get_auth_plugins()
155 enabled_plugins = SettingsModel().get_auth_plugins() or default_plugins
156
155
157 # Create template context and render it.
156 # Create template context and render it.
158 template_context = {
157 template_context = {
General Comments 0
You need to be logged in to leave comments. Login now