##// END OF EJS Templates
authentication: fixed for python3 migrations
super-admin -
r5057:c3d4b375 default
parent child Browse files
Show More
@@ -21,7 +21,7 b''
21 import logging
21 import logging
22 import importlib
22 import importlib
23
23
24 from pyramid.authentication import SessionAuthenticationPolicy
24 from pyramid.authentication import SessionAuthenticationHelper
25
25
26 from rhodecode.authentication.registry import AuthenticationPluginRegistry
26 from rhodecode.authentication.registry import AuthenticationPluginRegistry
27 from rhodecode.authentication.routes import root_factory
27 from rhodecode.authentication.routes import root_factory
@@ -70,9 +70,7 b' def discover_legacy_plugins(config, pref'
70
70
71 def includeme(config):
71 def includeme(config):
72
72
73 # Set authentication policy.
73 config.set_security_policy(SessionAuthenticationHelper())
74 authn_policy = SessionAuthenticationPolicy()
75 config.set_authentication_policy(authn_policy)
76
74
77 # Create authentication plugin registry and add it to the pyramid registry.
75 # Create authentication plugin registry and add it to the pyramid registry.
78 authn_registry = AuthenticationPluginRegistry(config.get_settings())
76 authn_registry = AuthenticationPluginRegistry(config.get_settings())
@@ -32,11 +32,13 b' import functools'
32
32
33 from pyramid.threadlocal import get_current_registry
33 from pyramid.threadlocal import get_current_registry
34
34
35 from rhodecode.authentication import AuthenticationPluginRegistry
35 from rhodecode.authentication.interface import IAuthnPluginRegistry
36 from rhodecode.authentication.interface import IAuthnPluginRegistry
36 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.lib import rc_cache
38 from rhodecode.lib import rc_cache
38 from rhodecode.lib.statsd_client import StatsdClient
39 from rhodecode.lib.statsd_client import StatsdClient
39 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
40 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
41 from rhodecode.lib.str_utils import safe_bytes
40 from rhodecode.lib.utils2 import safe_int, safe_str
42 from rhodecode.lib.utils2 import safe_int, safe_str
41 from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
43 from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
42 from rhodecode.model.db import User
44 from rhodecode.model.db import User
@@ -377,12 +379,13 b' class RhodeCodeAuthPluginBase(object):'
377 def get_user(self, username=None, **kwargs):
379 def get_user(self, username=None, **kwargs):
378 """
380 """
379 Helper method for user fetching in plugins, by default it's using
381 Helper method for user fetching in plugins, by default it's using
380 simple fetch by username, but this method can be custimized in plugins
382 simple fetch by username, but this method can be customized in plugins
381 eg. headers auth plugin to fetch user by environ params
383 eg. headers auth plugin to fetch user by environ params
382
384
383 :param username: username if given to fetch from database
385 :param username: username if given to fetch from database
384 :param kwargs: extra arguments needed for user fetching.
386 :param kwargs: extra arguments needed for user fetching.
385 """
387 """
388
386 user = None
389 user = None
387 log.debug(
390 log.debug(
388 'Trying to fetch user `%s` from RhodeCode database', username)
391 'Trying to fetch user `%s` from RhodeCode database', username)
@@ -437,6 +440,7 b' class RhodeCodeAuthPluginBase(object):'
437 # check if hash should be migrated ?
440 # check if hash should be migrated ?
438 new_hash = auth.get('_hash_migrate')
441 new_hash = auth.get('_hash_migrate')
439 if new_hash:
442 if new_hash:
443 # new_hash is a newly encrypted destination hash
440 self._migrate_hash_to_bcrypt(username, passwd, new_hash)
444 self._migrate_hash_to_bcrypt(username, passwd, new_hash)
441 if 'user_group_sync' not in auth:
445 if 'user_group_sync' not in auth:
442 auth['user_group_sync'] = False
446 auth['user_group_sync'] = False
@@ -446,9 +450,9 b' class RhodeCodeAuthPluginBase(object):'
446 def _migrate_hash_to_bcrypt(self, username, password, new_hash):
450 def _migrate_hash_to_bcrypt(self, username, password, new_hash):
447 new_hash_cypher = _RhodeCodeCryptoBCrypt()
451 new_hash_cypher = _RhodeCodeCryptoBCrypt()
448 # extra checks, so make sure new hash is correct.
452 # extra checks, so make sure new hash is correct.
449 password_encoded = safe_str(password)
453 password_as_bytes = safe_bytes(password)
450 if new_hash and new_hash_cypher.hash_check(
454
451 password_encoded, new_hash):
455 if new_hash and new_hash_cypher.hash_check(password_as_bytes, new_hash):
452 cur_user = User.get_by_username(username)
456 cur_user = User.get_by_username(username)
453 cur_user.password = new_hash
457 cur_user.password = new_hash
454 Session().add(cur_user)
458 Session().add(cur_user)
@@ -671,7 +675,7 b' def loadplugin(plugin_id):'
671 return plugin
675 return plugin
672
676
673
677
674 def get_authn_registry(registry=None):
678 def get_authn_registry(registry=None) -> AuthenticationPluginRegistry:
675 registry = registry or get_current_registry()
679 registry = registry or get_current_registry()
676 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
680 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
677 return authn_registry
681 return authn_registry
@@ -688,18 +692,23 b' def authenticate(username, password, env'
688 :param environ: environ headers passed for headers auth
692 :param environ: environ headers passed for headers auth
689 :param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
693 :param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
690 :param skip_missing: ignores plugins that are in db but not in environment
694 :param skip_missing: ignores plugins that are in db but not in environment
695 :param registry: pyramid registry
696 :param acl_repo_name: name of repo for ACL checks
691 :returns: None if auth failed, plugin_user dict if auth is correct
697 :returns: None if auth failed, plugin_user dict if auth is correct
692 """
698 """
693 if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
699 if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
694 raise ValueError('auth type must be on of http, vcs got "%s" instead'
700 raise ValueError(f'auth type must be on of http, vcs got "{auth_type}" instead')
695 % auth_type)
701
696 headers_only = environ and not (username and password)
702 auth_credentials = (username and password)
703 headers_only = environ and not auth_credentials
697
704
698 authn_registry = get_authn_registry(registry)
705 authn_registry = get_authn_registry(registry)
699
706
700 plugins_to_check = authn_registry.get_plugins_for_authentication()
707 plugins_to_check = authn_registry.get_plugins_for_authentication()
708 log.debug('authentication: headers=%s, username_and_passwd=%s', headers_only, bool(auth_credentials))
701 log.debug('Starting ordered authentication chain using %s plugins',
709 log.debug('Starting ordered authentication chain using %s plugins',
702 [x.name for x in plugins_to_check])
710 [x.name for x in plugins_to_check])
711
703 for plugin in plugins_to_check:
712 for plugin in plugins_to_check:
704 plugin.set_auth_type(auth_type)
713 plugin.set_auth_type(auth_type)
705 plugin.set_calling_scope_repo(acl_repo_name)
714 plugin.set_calling_scope_repo(acl_repo_name)
@@ -26,7 +26,9 b' RhodeCode authentication plugin for Atla'
26 import colander
26 import colander
27 import base64
27 import base64
28 import logging
28 import logging
29 import urllib.request, urllib.error, urllib.parse
29 import urllib.request
30 import urllib.error
31 import urllib.parse
30
32
31 from rhodecode.translation import _
33 from rhodecode.translation import _
32 from rhodecode.authentication.base import (
34 from rhodecode.authentication.base import (
@@ -27,7 +27,8 b' from rhodecode.authentication.base impor'
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
29 from rhodecode.lib.colander_utils import strip_whitespace
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.lib.utils2 import str2bool, safe_unicode
30 from rhodecode.lib.str_utils import safe_str
31 from rhodecode.lib.utils2 import str2bool
31 from rhodecode.model.db import User
32 from rhodecode.model.db import User
32
33
33
34
@@ -149,7 +150,7 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
149 def get_user(self, username=None, **kwargs):
150 def get_user(self, username=None, **kwargs):
150 """
151 """
151 Helper method for user fetching in plugins, by default it's using
152 Helper method for user fetching in plugins, by default it's using
152 simple fetch by username, but this method can be custimized in plugins
153 simple fetch by username, but this method can be customized in plugins
153 eg. headers auth plugin to fetch user by environ params
154 eg. headers auth plugin to fetch user by environ params
154 :param username: username if given to fetch
155 :param username: username if given to fetch
155 :param kwargs: extra arguments needed for user fetching.
156 :param kwargs: extra arguments needed for user fetching.
@@ -162,7 +163,7 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
162
163
163 def auth(self, userobj, username, password, settings, **kwargs):
164 def auth(self, userobj, username, password, settings, **kwargs):
164 """
165 """
165 Get's the headers_auth username (or email). It tries to get username
166 Gets the headers_auth username (or email). It tries to get username
166 from REMOTE_USER if this plugin is enabled, if that fails
167 from REMOTE_USER if this plugin is enabled, if that fails
167 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
168 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
168 is set. clean_username extracts the username from this data if it's
169 is set. clean_username extracts the username from this data if it's
@@ -210,8 +211,8 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
210
211
211 user_attrs = {
212 user_attrs = {
212 'username': username,
213 'username': username,
213 'firstname': safe_unicode(firstname or username),
214 'firstname': safe_str(firstname or username),
214 'lastname': safe_unicode(lastname or ''),
215 'lastname': safe_str(lastname or ''),
215 'groups': [],
216 'groups': [],
216 'user_group_sync': False,
217 'user_group_sync': False,
217 'email': email or '',
218 'email': email or '',
@@ -27,8 +27,10 b' http://www.jasig.org/cas'
27 import colander
27 import colander
28 import logging
28 import logging
29 import rhodecode
29 import rhodecode
30 import urllib.request, urllib.parse, urllib.error
30 import urllib.request
31 import urllib.request, urllib.error, urllib.parse
31 import urllib.parse
32 import urllib.error
33
32
34
33 from rhodecode.translation import _
35 from rhodecode.translation import _
34 from rhodecode.authentication.base import (
36 from rhodecode.authentication.base import (
@@ -36,8 +38,8 b' from rhodecode.authentication.base impor'
36 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.authentication.routes import AuthnPluginResourceBase
39 from rhodecode.authentication.routes import AuthnPluginResourceBase
38 from rhodecode.lib.colander_utils import strip_whitespace
40 from rhodecode.lib.colander_utils import strip_whitespace
39 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.model.db import User
41 from rhodecode.model.db import User
42 from rhodecode.lib.str_utils import safe_str
41
43
42 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
43
45
@@ -133,12 +135,12 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
133 {"url": url, "body": params, "headers": headers})
135 {"url": url, "body": params, "headers": headers})
134 request = urllib.request.Request(url, params, headers)
136 request = urllib.request.Request(url, params, headers)
135 try:
137 try:
136 response = urllib.request.urlopen(request)
138 urllib.request.urlopen(request)
137 except urllib.error.HTTPError as e:
139 except urllib.error.HTTPError as e:
138 log.debug("HTTPError when requesting Jasig CAS (status code: %d)", e.code)
140 log.debug("HTTPError when requesting Jasig CAS (status code: %d)", e.code)
139 return None
141 return None
140 except urllib.error.URLError as e:
142 except urllib.error.URLError as e:
141 log.debug("URLError when requesting Jasig CAS url: %s ", url)
143 log.debug("URLError when requesting Jasig CAS url: %s %s", url, e)
142 return None
144 return None
143
145
144 # old attrs fetched from RhodeCode database
146 # old attrs fetched from RhodeCode database
@@ -152,8 +154,8 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
152
154
153 user_attrs = {
155 user_attrs = {
154 'username': username,
156 'username': username,
155 'firstname': safe_unicode(firstname or username),
157 'firstname': safe_str(firstname or username),
156 'lastname': safe_unicode(lastname or ''),
158 'lastname': safe_str(lastname or ''),
157 'groups': [],
159 'groups': [],
158 'user_group_sync': False,
160 'user_group_sync': False,
159 'email': email or '',
161 'email': email or '',
@@ -34,7 +34,7 b' from rhodecode.lib.colander_utils import'
34 from rhodecode.lib.exceptions import (
34 from rhodecode.lib.exceptions import (
35 LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
35 LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
36 )
36 )
37 from rhodecode.lib.utils2 import safe_unicode, safe_str
37 from rhodecode.lib.str_utils import safe_str
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.validators import Missing
39 from rhodecode.model.validators import Missing
40
40
@@ -520,8 +520,8 b' class RhodeCodeAuthPlugin(RhodeCodeExter'
520
520
521 user_attrs = {
521 user_attrs = {
522 'username': username,
522 'username': username,
523 'firstname': safe_unicode(get_ldap_attr('attr_firstname') or firstname),
523 'firstname': safe_str(get_ldap_attr('attr_firstname') or firstname),
524 'lastname': safe_unicode(get_ldap_attr('attr_lastname') or lastname),
524 'lastname': safe_str(get_ldap_attr('attr_lastname') or lastname),
525 'groups': groups,
525 'groups': groups,
526 'user_group_sync': False,
526 'user_group_sync': False,
527 'email': get_ldap_attr('attr_email') or email,
527 'email': get_ldap_attr('attr_email') or email,
@@ -27,7 +27,7 b' import logging'
27 import colander
27 import colander
28
28
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30 from rhodecode.lib.utils2 import safe_str
30 from rhodecode.lib.utils2 import safe_bytes
31 from rhodecode.model.db import User
31 from rhodecode.model.db import User
32 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
32 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
33 from rhodecode.authentication.base import (
33 from rhodecode.authentication.base import (
@@ -153,7 +153,7 b' class RhodeCodeAuthPlugin(RhodeCodeAuthP'
153 if userobj.active:
153 if userobj.active:
154 from rhodecode.lib import auth
154 from rhodecode.lib import auth
155 crypto_backend = auth.crypto_backend()
155 crypto_backend = auth.crypto_backend()
156 password_encoded = safe_str(password)
156 password_encoded = safe_bytes(password)
157 password_match, new_hash = crypto_backend.hash_check_with_upgrade(
157 password_match, new_hash = crypto_backend.hash_check_with_upgrade(
158 password_encoded, userobj.password or '')
158 password_encoded, userobj.password or '')
159
159
@@ -66,25 +66,19 b' class AuthenticationPluginRegistry(objec'
66 if plugin.uid == plugin_uid:
66 if plugin.uid == plugin_uid:
67 return plugin
67 return plugin
68
68
69 def get_plugins_for_authentication(self, cache=True):
69 def get_cache_call_method(self, cache=True):
70 """
70 region, _ns = self.get_cache_region()
71 Returns a list of plugins which should be consulted when authenticating
72 a user. It only returns plugins which are enabled and active.
73 Additionally it includes the fallback plugin from the INI file, if
74 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
75 """
76
77 cache_namespace_uid = 'cache_auth_plugins'
78 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
79
71
80 @region.conditional_cache_on_arguments(condition=cache)
72 @region.conditional_cache_on_arguments(condition=cache)
81 def _get_auth_plugins(name, key, fallback_plugin):
73 def _get_auth_plugins(name: str, key: str, fallback_plugin):
82 plugins = []
74 log.debug('auth-plugins: calculating plugins available for authentication')
83
75
76 _plugins = []
84 # Add all enabled and active plugins to the list. We iterate over the
77 # Add all enabled and active plugins to the list. We iterate over the
85 # auth_plugins setting from DB because it also represents the ordering.
78 # auth_plugins setting from DB because it also represents the ordering.
86 enabled_plugins = SettingsModel().get_auth_plugins()
79 enabled_plugins = SettingsModel().get_auth_plugins()
87 raw_settings = SettingsModel().get_all_settings(cache=False)
80 raw_settings = SettingsModel().get_all_settings(cache=False)
81
88 for plugin_id in enabled_plugins:
82 for plugin_id in enabled_plugins:
89 plugin = self.get_plugin(plugin_id)
83 plugin = self.get_plugin(plugin_id)
90 if plugin is not None and plugin.is_active(
84 if plugin is not None and plugin.is_active(
@@ -92,7 +86,7 b' class AuthenticationPluginRegistry(objec'
92
86
93 # inject settings into plugin, we can re-use the DB fetched settings here
87 # inject settings into plugin, we can re-use the DB fetched settings here
94 plugin._settings = plugin._propagate_settings(raw_settings)
88 plugin._settings = plugin._propagate_settings(raw_settings)
95 plugins.append(plugin)
89 _plugins.append(plugin)
96
90
97 # Add the fallback plugin from ini file.
91 # Add the fallback plugin from ini file.
98 if fallback_plugin:
92 if fallback_plugin:
@@ -100,10 +94,22 b' class AuthenticationPluginRegistry(objec'
100 'Using fallback authentication plugin from INI file: "%s"',
94 'Using fallback authentication plugin from INI file: "%s"',
101 fallback_plugin)
95 fallback_plugin)
102 plugin = self.get_plugin(fallback_plugin)
96 plugin = self.get_plugin(fallback_plugin)
103 if plugin is not None and plugin not in plugins:
97 if plugin is not None and plugin not in _plugins:
104 plugin._settings = plugin._propagate_settings(raw_settings)
98 plugin._settings = plugin._propagate_settings(raw_settings)
105 plugins.append(plugin)
99 _plugins.append(plugin)
106 return plugins
100 return _plugins
101
102 return _get_auth_plugins
103
104 def get_plugins_for_authentication(self, cache=True):
105 """
106 Returns a list of plugins which should be consulted when authenticating
107 a user. It only returns plugins which are enabled and active.
108 Additionally, it includes the fallback plugin from the INI file, if
109 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
110 """
111
112 _get_auth_plugins = self.get_cache_call_method(cache=cache)
107
113
108 start = time.time()
114 start = time.time()
109 plugins = _get_auth_plugins('rhodecode_auth_plugins', 'v1', self._fallback_plugin)
115 plugins = _get_auth_plugins('rhodecode_auth_plugins', 'v1', self._fallback_plugin)
@@ -119,3 +125,17 b' class AuthenticationPluginRegistry(objec'
119
125
120 return plugins
126 return plugins
121
127
128 @classmethod
129 def get_cache_region(cls):
130 cache_namespace_uid = 'auth_plugins'
131 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
132 return region, cache_namespace_uid
133
134 @classmethod
135 def invalidate_auth_plugins_cache(cls, hard=True):
136 region, namespace_key = cls.get_cache_region()
137 log.debug('Invalidation cache [%s] region %s for cache_key: %s',
138 'invalidate_auth_plugins_cache', region, namespace_key)
139
140 # we use hard cleanup if invalidation is sent
141 rc_cache.clear_cache_namespace(region, namespace_key, method=rc_cache.CLEAR_DELETE)
General Comments 0
You need to be logged in to leave comments. Login now