##// END OF EJS Templates
Authentication: cache plugins for auth and their settings in the auth_registry....
marcink -
r4220:5a873939 stable
parent child Browse files
Show More
@@ -108,7 +108,8 b' class TestLoginController(object):'
108
108
109 def test_login_regular_forbidden_when_super_admin_restriction(self):
109 def test_login_regular_forbidden_when_super_admin_restriction(self):
110 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
110 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
111 with fixture.auth_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
111 with fixture.auth_restriction(self.app._pyramid_registry,
112 RhodeCodeAuthPlugin.AUTH_RESTRICTION_SUPER_ADMIN):
112 response = self.app.post(route_path('login'),
113 response = self.app.post(route_path('login'),
113 {'username': 'test_regular',
114 {'username': 'test_regular',
114 'password': 'test12'})
115 'password': 'test12'})
@@ -118,7 +119,8 b' class TestLoginController(object):'
118
119
119 def test_login_regular_forbidden_when_scope_restriction(self):
120 def test_login_regular_forbidden_when_scope_restriction(self):
120 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
121 from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin
121 with fixture.scope_restriction(RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
122 with fixture.scope_restriction(self.app._pyramid_registry,
123 RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_VCS):
122 response = self.app.post(route_path('login'),
124 response = self.app.post(route_path('login'),
123 {'username': 'test_regular',
125 {'username': 'test_regular',
124 'password': 'test12'})
126 'password': 'test12'})
@@ -150,6 +150,7 b' class RhodeCodeAuthPluginBase(object):'
150
150
151 def __init__(self, plugin_id):
151 def __init__(self, plugin_id):
152 self._plugin_id = plugin_id
152 self._plugin_id = plugin_id
153 self._settings = {}
153
154
154 def __str__(self):
155 def __str__(self):
155 return self.get_id()
156 return self.get_id()
@@ -226,17 +227,26 b' class RhodeCodeAuthPluginBase(object):'
226 """
227 """
227 return AuthnPluginSettingsSchemaBase()
228 return AuthnPluginSettingsSchemaBase()
228
229
229 def get_settings(self):
230 def _propagate_settings(self, raw_settings):
230 """
231 Returns the plugin settings as dictionary.
232 """
233 settings = {}
231 settings = {}
234 raw_settings = SettingsModel().get_all_settings()
235 for node in self.get_settings_schema():
232 for node in self.get_settings_schema():
236 settings[node.name] = self.get_setting_by_name(
233 settings[node.name] = self.get_setting_by_name(
237 node.name, plugin_cached_settings=raw_settings)
234 node.name, plugin_cached_settings=raw_settings)
238 return settings
235 return settings
239
236
237 def get_settings(self, use_cache=True):
238 """
239 Returns the plugin settings as dictionary.
240 """
241 if self._settings != {} and use_cache:
242 return self._settings
243
244 raw_settings = SettingsModel().get_all_settings()
245 settings = self._propagate_settings(raw_settings)
246
247 self._settings = settings
248 return self._settings
249
240 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
250 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
241 """
251 """
242 Returns a plugin setting by name.
252 Returns a plugin setting by name.
@@ -667,7 +677,7 b' def loadplugin(plugin_id):'
667
677
668 def get_authn_registry(registry=None):
678 def get_authn_registry(registry=None):
669 registry = registry or get_current_registry()
679 registry = registry or get_current_registry()
670 authn_registry = registry.getUtility(IAuthnPluginRegistry)
680 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
671 return authn_registry
681 return authn_registry
672
682
673
683
@@ -690,6 +700,7 b' def authenticate(username, password, env'
690 headers_only = environ and not (username and password)
700 headers_only = environ and not (username and password)
691
701
692 authn_registry = get_authn_registry(registry)
702 authn_registry = get_authn_registry(registry)
703
693 plugins_to_check = authn_registry.get_plugins_for_authentication()
704 plugins_to_check = authn_registry.get_plugins_for_authentication()
694 log.debug('Starting ordered authentication chain using %s plugins',
705 log.debug('Starting ordered authentication chain using %s plugins',
695 [x.name for x in plugins_to_check])
706 [x.name for x in plugins_to_check])
@@ -38,6 +38,7 b' class AuthenticationPluginRegistry(objec'
38
38
39 def __init__(self, settings):
39 def __init__(self, settings):
40 self._plugins = {}
40 self._plugins = {}
41 self._plugins_for_auth = None
41 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
42 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
42
43
43 def add_authn_plugin(self, config, plugin):
44 def add_authn_plugin(self, config, plugin):
@@ -63,6 +64,10 b' class AuthenticationPluginRegistry(objec'
63 if plugin.uid == plugin_uid:
64 if plugin.uid == plugin_uid:
64 return plugin
65 return plugin
65
66
67 def invalidate_plugins_for_auth(self):
68 log.debug('Invalidating cached plugins for authentication')
69 self._plugins_for_auth = None
70
66 def get_plugins_for_authentication(self):
71 def get_plugins_for_authentication(self):
67 """
72 """
68 Returns a list of plugins which should be consulted when authenticating
73 Returns a list of plugins which should be consulted when authenticating
@@ -70,6 +75,9 b' class AuthenticationPluginRegistry(objec'
70 Additionally it includes the fallback plugin from the INI file, if
75 Additionally it includes the fallback plugin from the INI file, if
71 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
76 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
72 """
77 """
78 if self._plugins_for_auth is not None:
79 return self._plugins_for_auth
80
73 plugins = []
81 plugins = []
74
82
75 # Add all enabled and active plugins to the list. We iterate over the
83 # Add all enabled and active plugins to the list. We iterate over the
@@ -80,6 +88,9 b' class AuthenticationPluginRegistry(objec'
80 plugin = self.get_plugin(plugin_id)
88 plugin = self.get_plugin(plugin_id)
81 if plugin is not None and plugin.is_active(
89 if plugin is not None and plugin.is_active(
82 plugin_cached_settings=raw_settings):
90 plugin_cached_settings=raw_settings):
91
92 # inject settings into plugin, we can re-use the DB fetched settings here
93 plugin._settings = plugin._propagate_settings(raw_settings)
83 plugins.append(plugin)
94 plugins.append(plugin)
84
95
85 # Add the fallback plugin from ini file.
96 # Add the fallback plugin from ini file.
@@ -89,6 +100,8 b' class AuthenticationPluginRegistry(objec'
89 self._fallback_plugin)
100 self._fallback_plugin)
90 plugin = self.get_plugin(self._fallback_plugin)
101 plugin = self.get_plugin(self._fallback_plugin)
91 if plugin is not None and plugin not in plugins:
102 if plugin is not None and plugin not in plugins:
103 plugin._settings = plugin._propagate_settings(raw_settings)
92 plugins.append(plugin)
104 plugins.append(plugin)
93
105
94 return plugins
106 self._plugins_for_auth = plugins
107 return self._plugins_for_auth
@@ -99,11 +99,12 b' class AuthnPluginViewBase(BaseAppView):'
99 for name, value in valid_data.items():
99 for name, value in valid_data.items():
100 self.plugin.create_or_update_setting(name, value)
100 self.plugin.create_or_update_setting(name, value)
101 Session().commit()
101 Session().commit()
102 SettingsModel().invalidate_settings_cache()
102
103
103 # Display success message and redirect.
104 # Display success message and redirect.
104 h.flash(_('Auth settings updated successfully.'), category='success')
105 h.flash(_('Auth settings updated successfully.'), category='success')
105 redirect_to = self.request.resource_path(
106 redirect_to = self.request.resource_path(self.context, route_name='auth_home')
106 self.context, route_name='auth_home')
107
107 return HTTPFound(redirect_to)
108 return HTTPFound(redirect_to)
108
109
109
110
@@ -159,7 +160,7 b' class AuthSettingsView(BaseAppView):'
159 'auth_plugins', plugins)
160 'auth_plugins', plugins)
160 Session().add(setting)
161 Session().add(setting)
161 Session().commit()
162 Session().commit()
162
163 SettingsModel().invalidate_settings_cache()
163 h.flash(_('Auth settings updated successfully.'), category='success')
164 h.flash(_('Auth settings updated successfully.'), category='success')
164 except formencode.Invalid as errors:
165 except formencode.Invalid as errors:
165 e = errors.error_dict or {}
166 e = errors.error_dict or {}
@@ -174,6 +175,6 b' class AuthSettingsView(BaseAppView):'
174 h.flash(_('Error occurred during update of auth settings.'),
175 h.flash(_('Error occurred during update of auth settings.'),
175 category='error')
176 category='error')
176
177
177 redirect_to = self.request.resource_path(
178 redirect_to = self.request.resource_path(self.context, route_name='auth_home')
178 self.context, route_name='auth_home')
179
179 return HTTPFound(redirect_to)
180 return HTTPFound(redirect_to)
@@ -211,8 +211,9 b' def vcs_operation_context('
211 class BasicAuth(AuthBasicAuthenticator):
211 class BasicAuth(AuthBasicAuthenticator):
212
212
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 initial_call_detection=False, acl_repo_name=None):
214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 self.realm = realm
215 self.realm = realm
216 self.rc_realm = rc_realm
216 self.initial_call = initial_call_detection
217 self.initial_call = initial_call_detection
217 self.authfunc = authfunc
218 self.authfunc = authfunc
218 self.registry = registry
219 self.registry = registry
@@ -227,7 +228,7 b' class BasicAuth(AuthBasicAuthenticator):'
227 return HTTPForbidden
228 return HTTPForbidden
228
229
229 def get_rc_realm(self):
230 def get_rc_realm(self):
230 return safe_str(self.registry.rhodecode_settings.get('rhodecode_realm'))
231 return safe_str(self.rc_realm)
231
232
232 def build_authentication(self):
233 def build_authentication(self):
233 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
@@ -133,15 +133,16 b' class SimpleVCS(object):'
133 self.config = config
133 self.config = config
134 # re-populated by specialized middleware
134 # re-populated by specialized middleware
135 self.repo_vcs_config = base.Config()
135 self.repo_vcs_config = base.Config()
136 self.rhodecode_settings = SettingsModel().get_all_settings(cache=True)
137
136
138 registry.rhodecode_settings = self.rhodecode_settings
137 rc_settings = SettingsModel().get_all_settings(cache=True, from_request=False)
138 realm = rc_settings.get('rhodecode_realm') or 'RhodeCode AUTH'
139
139 # authenticate this VCS request using authfunc
140 # authenticate this VCS request using authfunc
140 auth_ret_code_detection = \
141 auth_ret_code_detection = \
141 str2bool(self.config.get('auth_ret_code_detection', False))
142 str2bool(self.config.get('auth_ret_code_detection', False))
142 self.authenticate = BasicAuth(
143 self.authenticate = BasicAuth(
143 '', authenticate, registry, config.get('auth_ret_code'),
144 '', authenticate, registry, config.get('auth_ret_code'),
144 auth_ret_code_detection)
145 auth_ret_code_detection, rc_realm=realm)
145 self.ip_addr = '0.0.0.0'
146 self.ip_addr = '0.0.0.0'
146
147
147 @LazyProperty
148 @LazyProperty
@@ -25,7 +25,7 b' import re'
25 from collections import namedtuple
25 from collections import namedtuple
26 from functools import wraps
26 from functools import wraps
27 import bleach
27 import bleach
28 from pyramid.threadlocal import get_current_request
28 from pyramid.threadlocal import get_current_request, get_current_registry
29
29
30 from rhodecode.lib import rc_cache
30 from rhodecode.lib import rc_cache
31 from rhodecode.lib.utils2 import (
31 from rhodecode.lib.utils2 import (
@@ -213,6 +213,8 b' class SettingsModel(BaseModel):'
213 CacheKey.set_invalidate(invalidation_namespace)
213 CacheKey.set_invalidate(invalidation_namespace)
214
214
215 def get_all_settings(self, cache=False, from_request=True):
215 def get_all_settings(self, cache=False, from_request=True):
216 from rhodecode.authentication.base import get_authn_registry
217
216 # defines if we use GLOBAL, or PER_REPO
218 # defines if we use GLOBAL, or PER_REPO
217 repo = self._get_repo(self.repo) if self.repo else None
219 repo = self._get_repo(self.repo) if self.repo else None
218 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
220 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
@@ -253,6 +255,11 b' class SettingsModel(BaseModel):'
253 # are anyway very short lived and it's a safest way.
255 # are anyway very short lived and it's a safest way.
254 region = rc_cache.get_or_create_region('sql_cache_short')
256 region = rc_cache.get_or_create_region('sql_cache_short')
255 region.invalidate()
257 region.invalidate()
258 registry = get_current_registry()
259 if registry:
260 authn_registry = get_authn_registry(registry)
261 if authn_registry:
262 authn_registry.invalidate_plugins_for_auth()
256
263
257 result = _get_all_settings('rhodecode_settings', key)
264 result = _get_all_settings('rhodecode_settings', key)
258 log.debug('Fetching app settings for key: %s took: %.4fs', key,
265 log.debug('Fetching app settings for key: %s took: %.4fs', key,
@@ -29,6 +29,7 b' import shutil'
29
29
30 import configobj
30 import configobj
31
31
32 from rhodecode.model.settings import SettingsModel
32 from rhodecode.tests import *
33 from rhodecode.tests import *
33 from rhodecode.model.db import Repository, User, RepoGroup, UserGroup, Gist, UserEmailMap
34 from rhodecode.model.db import Repository, User, RepoGroup, UserGroup, Gist, UserEmailMap
34 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
@@ -122,7 +123,7 b' class Fixture(object):'
122
123
123 return context()
124 return context()
124
125
125 def auth_restriction(self, auth_restriction):
126 def auth_restriction(self, registry, auth_restriction):
126 """
127 """
127 Context process for changing the builtin rhodecode plugin auth restrictions.
128 Context process for changing the builtin rhodecode plugin auth restrictions.
128 Use like:
129 Use like:
@@ -135,26 +136,26 b' class Fixture(object):'
135
136
136 class context(object):
137 class context(object):
137 def _get_pluing(self):
138 def _get_pluing(self):
138 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(
139 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
139 RhodeCodeAuthPlugin.uid)
140 plugin = RhodeCodeAuthPlugin(plugin_id)
140 plugin = RhodeCodeAuthPlugin(plugin_id)
141 return plugin
141 return plugin
142
142
143 def __enter__(self):
143 def __enter__(self):
144 plugin = self._get_pluing()
144 plugin = self._get_pluing()
145 plugin.create_or_update_setting(
145 plugin.create_or_update_setting('auth_restriction', auth_restriction)
146 'auth_restriction', auth_restriction)
147 Session().commit()
146 Session().commit()
147 SettingsModel().invalidate_settings_cache()
148
148
149 def __exit__(self, exc_type, exc_val, exc_tb):
149 def __exit__(self, exc_type, exc_val, exc_tb):
150 plugin = self._get_pluing()
150 plugin = self._get_pluing()
151 plugin.create_or_update_setting(
151 plugin.create_or_update_setting(
152 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE)
152 'auth_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_NONE)
153 Session().commit()
153 Session().commit()
154 SettingsModel().invalidate_settings_cache()
154
155
155 return context()
156 return context()
156
157
157 def scope_restriction(self, scope_restriction):
158 def scope_restriction(self, registry, scope_restriction):
158 """
159 """
159 Context process for changing the builtin rhodecode plugin scope restrictions.
160 Context process for changing the builtin rhodecode plugin scope restrictions.
160 Use like:
161 Use like:
@@ -167,22 +168,22 b' class Fixture(object):'
167
168
168 class context(object):
169 class context(object):
169 def _get_pluing(self):
170 def _get_pluing(self):
170 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(
171 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
171 RhodeCodeAuthPlugin.uid)
172 plugin = RhodeCodeAuthPlugin(plugin_id)
172 plugin = RhodeCodeAuthPlugin(plugin_id)
173 return plugin
173 return plugin
174
174
175 def __enter__(self):
175 def __enter__(self):
176 plugin = self._get_pluing()
176 plugin = self._get_pluing()
177 plugin.create_or_update_setting(
177 plugin.create_or_update_setting('scope_restriction', scope_restriction)
178 'scope_restriction', scope_restriction)
179 Session().commit()
178 Session().commit()
179 SettingsModel().invalidate_settings_cache()
180
180
181 def __exit__(self, exc_type, exc_val, exc_tb):
181 def __exit__(self, exc_type, exc_val, exc_tb):
182 plugin = self._get_pluing()
182 plugin = self._get_pluing()
183 plugin.create_or_update_setting(
183 plugin.create_or_update_setting(
184 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL)
184 'scope_restriction', RhodeCodeAuthPlugin.AUTH_RESTRICTION_SCOPE_ALL)
185 Session().commit()
185 Session().commit()
186 SettingsModel().invalidate_settings_cache()
186
187
187 return context()
188 return context()
188
189
@@ -209,6 +209,8 b' def enable_auth_plugins(request, baseapp'
209 Session().add(setting)
209 Session().add(setting)
210 Session().commit()
210 Session().commit()
211
211
212 SettingsModel().invalidate_settings_cache()
213
212 def cleanup():
214 def cleanup():
213 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
215 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
214
216
General Comments 0
You need to be logged in to leave comments. Login now