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