diff --git a/rhodecode/apps/login/tests/test_login.py b/rhodecode/apps/login/tests/test_login.py --- a/rhodecode/apps/login/tests/test_login.py +++ b/rhodecode/apps/login/tests/test_login.py @@ -107,6 +107,16 @@ class TestLoginController(object): response.mustcontain('/%s' % HG_REPO) + def test_login_regular_forbidden_when_super_admin_restriction(self): + from rhodecode.authentication.plugins.auth_rhodecode import RhodeCodeAuthPlugin + with fixture.login_restriction(RhodeCodeAuthPlugin.LOGIN_RESTRICTION_SUPER_ADMIN): + response = self.app.post(route_path('login'), + {'username': 'test_regular', + 'password': 'test12'}) + + response.mustcontain('invalid user name') + response.mustcontain('invalid password') + def test_login_ok_came_from(self): test_came_from = '/_admin/users?branch=stable' _url = '{}?came_from={}'.format(route_path('login'), test_came_from) diff --git a/rhodecode/authentication/plugins/auth_rhodecode.py b/rhodecode/authentication/plugins/auth_rhodecode.py --- a/rhodecode/authentication/plugins/auth_rhodecode.py +++ b/rhodecode/authentication/plugins/auth_rhodecode.py @@ -46,20 +46,10 @@ class RhodecodeAuthnResource(AuthnPlugin pass -class RhodeCodeSettingsSchema(AuthnPluginSettingsSchemaBase): - - superadmin_restriction = colander.SchemaNode( - colander.Bool(), - default=False, - description=_('Only allow super-admins to log-in using this plugin.'), - missing=False, - title=_('Enabled'), - widget='bool', - ) - - class RhodeCodeAuthPlugin(RhodeCodeAuthPluginBase): uid = 'rhodecode' + LOGIN_RESTRICTION_NONE = 'none' + LOGIN_RESTRICTION_SUPER_ADMIN = 'super_admin' def includeme(self, config): config.add_authn_plugin(self) @@ -112,12 +102,20 @@ class RhodeCodeAuthPlugin(RhodeCodeAuthP if not userobj: log.debug('userobj was:%s skipping', userobj) return None + if userobj.extern_type != self.name: log.warning( "userobj:%s extern_type mismatch got:`%s` expected:`%s`", userobj, userobj.extern_type, self.name) return None + login_restriction = settings.get('login_restriction', '') + if login_restriction == self.LOGIN_RESTRICTION_SUPER_ADMIN and userobj.admin is False: + log.info( + "userobj:%s is not super-admin and login restriction is set to %s", + userobj, login_restriction) + return None + user_attrs = { "username": userobj.username, "firstname": userobj.firstname, @@ -149,8 +147,8 @@ class RhodeCodeAuthPlugin(RhodeCodeAuthP user_attrs['_hash_migrate'] = new_hash if userobj.username == User.DEFAULT_USER and userobj.active: - log.info( - 'user `%s` authenticated correctly as anonymous user', userobj.username) + log.info('user `%s` authenticated correctly as anonymous user', + userobj.username) return user_attrs elif userobj.username == username and password_match: @@ -166,6 +164,23 @@ class RhodeCodeAuthPlugin(RhodeCodeAuthP return None +class RhodeCodeSettingsSchema(AuthnPluginSettingsSchemaBase): + login_restriction_choices = [ + (RhodeCodeAuthPlugin.LOGIN_RESTRICTION_NONE, 'All users'), + (RhodeCodeAuthPlugin.LOGIN_RESTRICTION_SUPER_ADMIN, 'Super admins only') + ] + + login_restriction = colander.SchemaNode( + colander.String(), + default=login_restriction_choices[0], + description=_('Choose login restrition for users.'), + title=_('Login restriction'), + validator=colander.OneOf([x[0] for x in login_restriction_choices]), + widget='select_with_labels', + choices=login_restriction_choices + ) + + def includeme(config): plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid) plugin_factory(plugin_id).includeme(config) diff --git a/rhodecode/templates/admin/auth/plugin_settings.mako b/rhodecode/templates/admin/auth/plugin_settings.mako --- a/rhodecode/templates/admin/auth/plugin_settings.mako +++ b/rhodecode/templates/admin/auth/plugin_settings.mako @@ -66,6 +66,8 @@
${h.checkbox(node.name, True, checked=defaults.get(node.name))}
%elif node.widget == "select": ${h.select(node.name, defaults.get(node.name), node.validator.choices, class_="select2AuthSetting")} + %elif node.widget == "select_with_labels": + ${h.select(node.name, defaults.get(node.name), node.choices, class_="select2AuthSetting")} %elif node.widget == "textarea":
${h.textarea(node.name, defaults.get(node.name), rows=10)}
%elif node.widget == "readonly": diff --git a/rhodecode/tests/fixture.py b/rhodecode/tests/fixture.py --- a/rhodecode/tests/fixture.py +++ b/rhodecode/tests/fixture.py @@ -38,6 +38,8 @@ from rhodecode.model.repo_group import R from rhodecode.model.user_group import UserGroupModel from rhodecode.model.gist import GistModel from rhodecode.model.auth_token import AuthTokenModel +from rhodecode.authentication.plugins.auth_rhodecode import \ + RhodeCodeAuthPlugin dn = os.path.dirname FIXTURES = os.path.join(dn(dn(os.path.abspath(__file__))), 'tests', 'fixtures') @@ -120,6 +122,38 @@ class Fixture(object): return context() + def login_restriction(self, login_restriction): + """ + Context process for changing the builtin rhodecode plugin login restrictions. + Use like: + fixture = Fixture() + with fixture.login_restriction('super_admin'): + #tests + + after this block login restriction will be taken off + """ + + class context(object): + def _get_pluing(self): + plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format( + RhodeCodeAuthPlugin.uid) + plugin = RhodeCodeAuthPlugin(plugin_id) + return plugin + + def __enter__(self): + plugin = self._get_pluing() + plugin.create_or_update_setting( + 'login_restriction', login_restriction) + Session().commit() + + def __exit__(self, exc_type, exc_val, exc_tb): + plugin = self._get_pluing() + plugin.create_or_update_setting( + 'login_restriction', RhodeCodeAuthPlugin.LOGIN_RESTRICTION_NONE) + Session().commit() + + return context() + def _get_repo_create_params(self, **custom): defs = { 'repo_name': None,