authentication: fixed for python3 migrations
This commit is contained in:
parent
d985e2f96c
commit
2ff61d5c42
8 changed files with 78 additions and 46 deletions
|
|
@ -21,7 +21,7 @@
|
|||
import logging
|
||||
import importlib
|
||||
|
||||
from pyramid.authentication import SessionAuthenticationPolicy
|
||||
from pyramid.authentication import SessionAuthenticationHelper
|
||||
|
||||
from rhodecode.authentication.registry import AuthenticationPluginRegistry
|
||||
from rhodecode.authentication.routes import root_factory
|
||||
|
|
@ -70,9 +70,7 @@ def discover_legacy_plugins(config, prefix=legacy_plugin_prefix):
|
|||
|
||||
def includeme(config):
|
||||
|
||||
# Set authentication policy.
|
||||
authn_policy = SessionAuthenticationPolicy()
|
||||
config.set_authentication_policy(authn_policy)
|
||||
config.set_security_policy(SessionAuthenticationHelper())
|
||||
|
||||
# Create authentication plugin registry and add it to the pyramid registry.
|
||||
authn_registry = AuthenticationPluginRegistry(config.get_settings())
|
||||
|
|
|
|||
|
|
@ -32,11 +32,13 @@ import functools
|
|||
|
||||
from pyramid.threadlocal import get_current_registry
|
||||
|
||||
from rhodecode.authentication import AuthenticationPluginRegistry
|
||||
from rhodecode.authentication.interface import IAuthnPluginRegistry
|
||||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.lib import rc_cache
|
||||
from rhodecode.lib.statsd_client import StatsdClient
|
||||
from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
|
||||
from rhodecode.lib.str_utils import safe_bytes
|
||||
from rhodecode.lib.utils2 import safe_int, safe_str
|
||||
from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
|
||||
from rhodecode.model.db import User
|
||||
|
|
@ -377,12 +379,13 @@ class RhodeCodeAuthPluginBase(object):
|
|||
def get_user(self, username=None, **kwargs):
|
||||
"""
|
||||
Helper method for user fetching in plugins, by default it's using
|
||||
simple fetch by username, but this method can be custimized in plugins
|
||||
simple fetch by username, but this method can be customized in plugins
|
||||
eg. headers auth plugin to fetch user by environ params
|
||||
|
||||
:param username: username if given to fetch from database
|
||||
:param kwargs: extra arguments needed for user fetching.
|
||||
"""
|
||||
|
||||
user = None
|
||||
log.debug(
|
||||
'Trying to fetch user `%s` from RhodeCode database', username)
|
||||
|
|
@ -437,6 +440,7 @@ class RhodeCodeAuthPluginBase(object):
|
|||
# check if hash should be migrated ?
|
||||
new_hash = auth.get('_hash_migrate')
|
||||
if new_hash:
|
||||
# new_hash is a newly encrypted destination hash
|
||||
self._migrate_hash_to_bcrypt(username, passwd, new_hash)
|
||||
if 'user_group_sync' not in auth:
|
||||
auth['user_group_sync'] = False
|
||||
|
|
@ -446,9 +450,9 @@ class RhodeCodeAuthPluginBase(object):
|
|||
def _migrate_hash_to_bcrypt(self, username, password, new_hash):
|
||||
new_hash_cypher = _RhodeCodeCryptoBCrypt()
|
||||
# extra checks, so make sure new hash is correct.
|
||||
password_encoded = safe_str(password)
|
||||
if new_hash and new_hash_cypher.hash_check(
|
||||
password_encoded, new_hash):
|
||||
password_as_bytes = safe_bytes(password)
|
||||
|
||||
if new_hash and new_hash_cypher.hash_check(password_as_bytes, new_hash):
|
||||
cur_user = User.get_by_username(username)
|
||||
cur_user.password = new_hash
|
||||
Session().add(cur_user)
|
||||
|
|
@ -671,7 +675,7 @@ def loadplugin(plugin_id):
|
|||
return plugin
|
||||
|
||||
|
||||
def get_authn_registry(registry=None):
|
||||
def get_authn_registry(registry=None) -> AuthenticationPluginRegistry:
|
||||
registry = registry or get_current_registry()
|
||||
authn_registry = registry.queryUtility(IAuthnPluginRegistry)
|
||||
return authn_registry
|
||||
|
|
@ -688,18 +692,23 @@ def authenticate(username, password, environ=None, auth_type=None,
|
|||
:param environ: environ headers passed for headers auth
|
||||
:param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
|
||||
:param skip_missing: ignores plugins that are in db but not in environment
|
||||
:param registry: pyramid registry
|
||||
:param acl_repo_name: name of repo for ACL checks
|
||||
:returns: None if auth failed, plugin_user dict if auth is correct
|
||||
"""
|
||||
if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
|
||||
raise ValueError('auth type must be on of http, vcs got "%s" instead'
|
||||
% auth_type)
|
||||
headers_only = environ and not (username and password)
|
||||
raise ValueError(f'auth type must be on of http, vcs got "{auth_type}" instead')
|
||||
|
||||
auth_credentials = (username and password)
|
||||
headers_only = environ and not auth_credentials
|
||||
|
||||
authn_registry = get_authn_registry(registry)
|
||||
|
||||
plugins_to_check = authn_registry.get_plugins_for_authentication()
|
||||
log.debug('authentication: headers=%s, username_and_passwd=%s', headers_only, bool(auth_credentials))
|
||||
log.debug('Starting ordered authentication chain using %s plugins',
|
||||
[x.name for x in plugins_to_check])
|
||||
|
||||
for plugin in plugins_to_check:
|
||||
plugin.set_auth_type(auth_type)
|
||||
plugin.set_calling_scope_repo(acl_repo_name)
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ RhodeCode authentication plugin for Atlassian CROWD
|
|||
import colander
|
||||
import base64
|
||||
import logging
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.authentication.base import (
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ from rhodecode.authentication.base import (
|
|||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.authentication.routes import AuthnPluginResourceBase
|
||||
from rhodecode.lib.colander_utils import strip_whitespace
|
||||
from rhodecode.lib.utils2 import str2bool, safe_unicode
|
||||
from rhodecode.lib.str_utils import safe_str
|
||||
from rhodecode.lib.utils2 import str2bool
|
||||
from rhodecode.model.db import User
|
||||
|
||||
|
||||
|
|
@ -149,7 +150,7 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
def get_user(self, username=None, **kwargs):
|
||||
"""
|
||||
Helper method for user fetching in plugins, by default it's using
|
||||
simple fetch by username, but this method can be custimized in plugins
|
||||
simple fetch by username, but this method can be customized in plugins
|
||||
eg. headers auth plugin to fetch user by environ params
|
||||
:param username: username if given to fetch
|
||||
:param kwargs: extra arguments needed for user fetching.
|
||||
|
|
@ -162,7 +163,7 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
|
||||
def auth(self, userobj, username, password, settings, **kwargs):
|
||||
"""
|
||||
Get's the headers_auth username (or email). It tries to get username
|
||||
Gets the headers_auth username (or email). It tries to get username
|
||||
from REMOTE_USER if this plugin is enabled, if that fails
|
||||
it tries to get username from HTTP_X_FORWARDED_USER if fallback header
|
||||
is set. clean_username extracts the username from this data if it's
|
||||
|
|
@ -210,8 +211,8 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
|
||||
user_attrs = {
|
||||
'username': username,
|
||||
'firstname': safe_unicode(firstname or username),
|
||||
'lastname': safe_unicode(lastname or ''),
|
||||
'firstname': safe_str(firstname or username),
|
||||
'lastname': safe_str(lastname or ''),
|
||||
'groups': [],
|
||||
'user_group_sync': False,
|
||||
'email': email or '',
|
||||
|
|
|
|||
|
|
@ -27,8 +27,10 @@ http://www.jasig.org/cas
|
|||
import colander
|
||||
import logging
|
||||
import rhodecode
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
|
||||
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.authentication.base import (
|
||||
|
|
@ -36,8 +38,8 @@ from rhodecode.authentication.base import (
|
|||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.authentication.routes import AuthnPluginResourceBase
|
||||
from rhodecode.lib.colander_utils import strip_whitespace
|
||||
from rhodecode.lib.utils2 import safe_unicode
|
||||
from rhodecode.model.db import User
|
||||
from rhodecode.lib.str_utils import safe_str
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -133,12 +135,12 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
{"url": url, "body": params, "headers": headers})
|
||||
request = urllib.request.Request(url, params, headers)
|
||||
try:
|
||||
response = urllib.request.urlopen(request)
|
||||
urllib.request.urlopen(request)
|
||||
except urllib.error.HTTPError as e:
|
||||
log.debug("HTTPError when requesting Jasig CAS (status code: %d)", e.code)
|
||||
return None
|
||||
except urllib.error.URLError as e:
|
||||
log.debug("URLError when requesting Jasig CAS url: %s ", url)
|
||||
log.debug("URLError when requesting Jasig CAS url: %s %s", url, e)
|
||||
return None
|
||||
|
||||
# old attrs fetched from RhodeCode database
|
||||
|
|
@ -152,8 +154,8 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
|
||||
user_attrs = {
|
||||
'username': username,
|
||||
'firstname': safe_unicode(firstname or username),
|
||||
'lastname': safe_unicode(lastname or ''),
|
||||
'firstname': safe_str(firstname or username),
|
||||
'lastname': safe_str(lastname or ''),
|
||||
'groups': [],
|
||||
'user_group_sync': False,
|
||||
'email': email or '',
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ from rhodecode.lib.colander_utils import strip_whitespace
|
|||
from rhodecode.lib.exceptions import (
|
||||
LdapConnectionError, LdapUsernameError, LdapPasswordError, LdapImportError
|
||||
)
|
||||
from rhodecode.lib.utils2 import safe_unicode, safe_str
|
||||
from rhodecode.lib.str_utils import safe_str
|
||||
from rhodecode.model.db import User
|
||||
from rhodecode.model.validators import Missing
|
||||
|
||||
|
|
@ -520,8 +520,8 @@ class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
|
|||
|
||||
user_attrs = {
|
||||
'username': username,
|
||||
'firstname': safe_unicode(get_ldap_attr('attr_firstname') or firstname),
|
||||
'lastname': safe_unicode(get_ldap_attr('attr_lastname') or lastname),
|
||||
'firstname': safe_str(get_ldap_attr('attr_firstname') or firstname),
|
||||
'lastname': safe_str(get_ldap_attr('attr_lastname') or lastname),
|
||||
'groups': groups,
|
||||
'user_group_sync': False,
|
||||
'email': get_ldap_attr('attr_email') or email,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import logging
|
|||
import colander
|
||||
|
||||
from rhodecode.translation import _
|
||||
from rhodecode.lib.utils2 import safe_str
|
||||
from rhodecode.lib.utils2 import safe_bytes
|
||||
from rhodecode.model.db import User
|
||||
from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
|
||||
from rhodecode.authentication.base import (
|
||||
|
|
@ -153,7 +153,7 @@ class RhodeCodeAuthPlugin(RhodeCodeAuthPluginBase):
|
|||
if userobj.active:
|
||||
from rhodecode.lib import auth
|
||||
crypto_backend = auth.crypto_backend()
|
||||
password_encoded = safe_str(password)
|
||||
password_encoded = safe_bytes(password)
|
||||
password_match, new_hash = crypto_backend.hash_check_with_upgrade(
|
||||
password_encoded, userobj.password or '')
|
||||
|
||||
|
|
|
|||
|
|
@ -66,25 +66,19 @@ class AuthenticationPluginRegistry(object):
|
|||
if plugin.uid == plugin_uid:
|
||||
return plugin
|
||||
|
||||
def get_plugins_for_authentication(self, cache=True):
|
||||
"""
|
||||
Returns a list of plugins which should be consulted when authenticating
|
||||
a user. It only returns plugins which are enabled and active.
|
||||
Additionally it includes the fallback plugin from the INI file, if
|
||||
`rhodecode.auth_plugin_fallback` is set to a plugin ID.
|
||||
"""
|
||||
|
||||
cache_namespace_uid = 'cache_auth_plugins'
|
||||
region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
|
||||
def get_cache_call_method(self, cache=True):
|
||||
region, _ns = self.get_cache_region()
|
||||
|
||||
@region.conditional_cache_on_arguments(condition=cache)
|
||||
def _get_auth_plugins(name, key, fallback_plugin):
|
||||
plugins = []
|
||||
def _get_auth_plugins(name: str, key: str, fallback_plugin):
|
||||
log.debug('auth-plugins: calculating plugins available for authentication')
|
||||
|
||||
_plugins = []
|
||||
# Add all enabled and active plugins to the list. We iterate over the
|
||||
# auth_plugins setting from DB because it also represents the ordering.
|
||||
enabled_plugins = SettingsModel().get_auth_plugins()
|
||||
raw_settings = SettingsModel().get_all_settings(cache=False)
|
||||
|
||||
for plugin_id in enabled_plugins:
|
||||
plugin = self.get_plugin(plugin_id)
|
||||
if plugin is not None and plugin.is_active(
|
||||
|
|
@ -92,7 +86,7 @@ class AuthenticationPluginRegistry(object):
|
|||
|
||||
# inject settings into plugin, we can re-use the DB fetched settings here
|
||||
plugin._settings = plugin._propagate_settings(raw_settings)
|
||||
plugins.append(plugin)
|
||||
_plugins.append(plugin)
|
||||
|
||||
# Add the fallback plugin from ini file.
|
||||
if fallback_plugin:
|
||||
|
|
@ -100,10 +94,22 @@ class AuthenticationPluginRegistry(object):
|
|||
'Using fallback authentication plugin from INI file: "%s"',
|
||||
fallback_plugin)
|
||||
plugin = self.get_plugin(fallback_plugin)
|
||||
if plugin is not None and plugin not in plugins:
|
||||
if plugin is not None and plugin not in _plugins:
|
||||
plugin._settings = plugin._propagate_settings(raw_settings)
|
||||
plugins.append(plugin)
|
||||
return plugins
|
||||
_plugins.append(plugin)
|
||||
return _plugins
|
||||
|
||||
return _get_auth_plugins
|
||||
|
||||
def get_plugins_for_authentication(self, cache=True):
|
||||
"""
|
||||
Returns a list of plugins which should be consulted when authenticating
|
||||
a user. It only returns plugins which are enabled and active.
|
||||
Additionally, it includes the fallback plugin from the INI file, if
|
||||
`rhodecode.auth_plugin_fallback` is set to a plugin ID.
|
||||
"""
|
||||
|
||||
_get_auth_plugins = self.get_cache_call_method(cache=cache)
|
||||
|
||||
start = time.time()
|
||||
plugins = _get_auth_plugins('rhodecode_auth_plugins', 'v1', self._fallback_plugin)
|
||||
|
|
@ -119,3 +125,17 @@ class AuthenticationPluginRegistry(object):
|
|||
|
||||
return plugins
|
||||
|
||||
@classmethod
|
||||
def get_cache_region(cls):
|
||||
cache_namespace_uid = 'auth_plugins'
|
||||
region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
|
||||
return region, cache_namespace_uid
|
||||
|
||||
@classmethod
|
||||
def invalidate_auth_plugins_cache(cls, hard=True):
|
||||
region, namespace_key = cls.get_cache_region()
|
||||
log.debug('Invalidation cache [%s] region %s for cache_key: %s',
|
||||
'invalidate_auth_plugins_cache', region, namespace_key)
|
||||
|
||||
# we use hard cleanup if invalidation is sent
|
||||
rc_cache.clear_cache_namespace(region, namespace_key, method=rc_cache.CLEAR_DELETE)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue