##// END OF EJS Templates
auth-plugins: updated display names of plugins, and root resource.
marcink -
r3234:83285ea3 default
parent child Browse files
Show More
@@ -1,143 +1,143 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 RhodeCode authentication plugin for built in internal auth
23 23 """
24 24
25 25 import logging
26 26
27 27 from rhodecode.translation import _
28 28
29 29 from rhodecode.authentication.base import RhodeCodeAuthPluginBase, hybrid_property
30 30 from rhodecode.authentication.routes import AuthnPluginResourceBase
31 31 from rhodecode.lib.utils2 import safe_str
32 32 from rhodecode.model.db import User
33 33
34 34 log = logging.getLogger(__name__)
35 35
36 36
37 37 def plugin_factory(plugin_id, *args, **kwds):
38 38 plugin = RhodeCodeAuthPlugin(plugin_id)
39 39 return plugin
40 40
41 41
42 42 class RhodecodeAuthnResource(AuthnPluginResourceBase):
43 43 pass
44 44
45 45
46 46 class RhodeCodeAuthPlugin(RhodeCodeAuthPluginBase):
47 47
48 48 def includeme(self, config):
49 49 config.add_authn_plugin(self)
50 50 config.add_authn_resource(self.get_id(), RhodecodeAuthnResource(self))
51 51 config.add_view(
52 52 'rhodecode.authentication.views.AuthnPluginViewBase',
53 53 attr='settings_get',
54 54 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
55 55 request_method='GET',
56 56 route_name='auth_home',
57 57 context=RhodecodeAuthnResource)
58 58 config.add_view(
59 59 'rhodecode.authentication.views.AuthnPluginViewBase',
60 60 attr='settings_post',
61 61 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
62 62 request_method='POST',
63 63 route_name='auth_home',
64 64 context=RhodecodeAuthnResource)
65 65
66 66 def get_display_name(self):
67 return _('Rhodecode')
67 return _('RhodeCode Internal')
68 68
69 69 @hybrid_property
70 70 def name(self):
71 71 return "rhodecode"
72 72
73 73 def user_activation_state(self):
74 74 def_user_perms = User.get_default_user().AuthUser().permissions['global']
75 75 return 'hg.register.auto_activate' in def_user_perms
76 76
77 77 def allows_authentication_from(
78 78 self, user, allows_non_existing_user=True,
79 79 allowed_auth_plugins=None, allowed_auth_sources=None):
80 80 """
81 81 Custom method for this auth that doesn't accept non existing users.
82 82 We know that user exists in our database.
83 83 """
84 84 allows_non_existing_user = False
85 85 return super(RhodeCodeAuthPlugin, self).allows_authentication_from(
86 86 user, allows_non_existing_user=allows_non_existing_user)
87 87
88 88 def auth(self, userobj, username, password, settings, **kwargs):
89 89 if not userobj:
90 90 log.debug('userobj was:%s skipping', userobj)
91 91 return None
92 92 if userobj.extern_type != self.name:
93 93 log.warning(
94 94 "userobj:%s extern_type mismatch got:`%s` expected:`%s`",
95 95 userobj, userobj.extern_type, self.name)
96 96 return None
97 97
98 98 user_attrs = {
99 99 "username": userobj.username,
100 100 "firstname": userobj.firstname,
101 101 "lastname": userobj.lastname,
102 102 "groups": [],
103 103 'user_group_sync': False,
104 104 "email": userobj.email,
105 105 "admin": userobj.admin,
106 106 "active": userobj.active,
107 107 "active_from_extern": userobj.active,
108 108 "extern_name": userobj.user_id,
109 109 "extern_type": userobj.extern_type,
110 110 }
111 111
112 112 log.debug("User attributes:%s", user_attrs)
113 113 if userobj.active:
114 114 from rhodecode.lib import auth
115 115 crypto_backend = auth.crypto_backend()
116 116 password_encoded = safe_str(password)
117 117 password_match, new_hash = crypto_backend.hash_check_with_upgrade(
118 118 password_encoded, userobj.password or '')
119 119
120 120 if password_match and new_hash:
121 121 log.debug('user %s properly authenticated, but '
122 122 'requires hash change to bcrypt', userobj)
123 123 # if password match, and we use OLD deprecated hash,
124 124 # we should migrate this user hash password to the new hash
125 125 # we store the new returned by hash_check_with_upgrade function
126 126 user_attrs['_hash_migrate'] = new_hash
127 127
128 128 if userobj.username == User.DEFAULT_USER and userobj.active:
129 129 log.info(
130 130 'user `%s` authenticated correctly as anonymous user', userobj.username)
131 131 return user_attrs
132 132
133 133 elif userobj.username == username and password_match:
134 134 log.info('user `%s` authenticated correctly', userobj.username)
135 135 return user_attrs
136 136 log.warn("user `%s` used a wrong password when "
137 137 "authenticating on this plugin", userobj.username)
138 138 return None
139 139 else:
140 140 log.warning(
141 141 'user `%s` failed to authenticate via %s, reason: account not '
142 142 'active.', username, self.name)
143 143 return None
@@ -1,151 +1,151 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 RhodeCode authentication token plugin for built in internal auth
23 23 """
24 24
25 25 import logging
26 26
27 27 from rhodecode.translation import _
28 28 from rhodecode.authentication.base import (
29 29 RhodeCodeAuthPluginBase, VCS_TYPE, hybrid_property)
30 30 from rhodecode.authentication.routes import AuthnPluginResourceBase
31 31 from rhodecode.model.db import User, UserApiKeys, Repository
32 32
33 33
34 34 log = logging.getLogger(__name__)
35 35
36 36
37 37 def plugin_factory(plugin_id, *args, **kwds):
38 38 plugin = RhodeCodeAuthPlugin(plugin_id)
39 39 return plugin
40 40
41 41
42 42 class RhodecodeAuthnResource(AuthnPluginResourceBase):
43 43 pass
44 44
45 45
46 46 class RhodeCodeAuthPlugin(RhodeCodeAuthPluginBase):
47 47 """
48 48 Enables usage of authentication tokens for vcs operations.
49 49 """
50 50
51 51 def includeme(self, config):
52 52 config.add_authn_plugin(self)
53 53 config.add_authn_resource(self.get_id(), RhodecodeAuthnResource(self))
54 54 config.add_view(
55 55 'rhodecode.authentication.views.AuthnPluginViewBase',
56 56 attr='settings_get',
57 57 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
58 58 request_method='GET',
59 59 route_name='auth_home',
60 60 context=RhodecodeAuthnResource)
61 61 config.add_view(
62 62 'rhodecode.authentication.views.AuthnPluginViewBase',
63 63 attr='settings_post',
64 64 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
65 65 request_method='POST',
66 66 route_name='auth_home',
67 67 context=RhodecodeAuthnResource)
68 68
69 69 def get_display_name(self):
70 return _('Rhodecode Token Auth')
70 return _('Rhodecode Token')
71 71
72 72 @classmethod
73 73 def docs(cls):
74 74 return "https://docs.rhodecode.com/RhodeCode-Enterprise/auth/auth-token.html"
75 75
76 76 @hybrid_property
77 77 def name(self):
78 78 return "authtoken"
79 79
80 80 def user_activation_state(self):
81 81 def_user_perms = User.get_default_user().AuthUser().permissions['global']
82 82 return 'hg.register.auto_activate' in def_user_perms
83 83
84 84 def allows_authentication_from(
85 85 self, user, allows_non_existing_user=True,
86 86 allowed_auth_plugins=None, allowed_auth_sources=None):
87 87 """
88 88 Custom method for this auth that doesn't accept empty users. And also
89 89 allows users from all other active plugins to use it and also
90 90 authenticate against it. But only via vcs mode
91 91 """
92 92 from rhodecode.authentication.base import get_authn_registry
93 93 authn_registry = get_authn_registry()
94 94
95 95 active_plugins = set(
96 96 [x.name for x in authn_registry.get_plugins_for_authentication()])
97 97 active_plugins.discard(self.name)
98 98
99 99 allowed_auth_plugins = [self.name] + list(active_plugins)
100 100 # only for vcs operations
101 101 allowed_auth_sources = [VCS_TYPE]
102 102
103 103 return super(RhodeCodeAuthPlugin, self).allows_authentication_from(
104 104 user, allows_non_existing_user=False,
105 105 allowed_auth_plugins=allowed_auth_plugins,
106 106 allowed_auth_sources=allowed_auth_sources)
107 107
108 108 def auth(self, userobj, username, password, settings, **kwargs):
109 109 if not userobj:
110 110 log.debug('userobj was:%s skipping', userobj)
111 111 return None
112 112
113 113 user_attrs = {
114 114 "username": userobj.username,
115 115 "firstname": userobj.firstname,
116 116 "lastname": userobj.lastname,
117 117 "groups": [],
118 118 'user_group_sync': False,
119 119 "email": userobj.email,
120 120 "admin": userobj.admin,
121 121 "active": userobj.active,
122 122 "active_from_extern": userobj.active,
123 123 "extern_name": userobj.user_id,
124 124 "extern_type": userobj.extern_type,
125 125 }
126 126
127 127 log.debug('Authenticating user with args %s', user_attrs)
128 128 if userobj.active:
129 129 # calling context repo for token scopes
130 130 scope_repo_id = None
131 131 if self.acl_repo_name:
132 132 repo = Repository.get_by_repo_name(self.acl_repo_name)
133 133 scope_repo_id = repo.repo_id if repo else None
134 134
135 135 token_match = userobj.authenticate_by_token(
136 136 password, roles=[UserApiKeys.ROLE_VCS],
137 137 scope_repo_id=scope_repo_id)
138 138
139 139 if userobj.username == username and token_match:
140 140 log.info(
141 141 'user `%s` successfully authenticated via %s',
142 142 user_attrs['username'], self.name)
143 143 return user_attrs
144 144 log.warn(
145 145 'user `%s` failed to authenticate via %s, reason: bad or '
146 146 'inactive token.', username, self.name)
147 147 else:
148 148 log.warning(
149 149 'user `%s` failed to authenticate via %s, reason: account not '
150 150 'active.', username, self.name)
151 151 return None
@@ -1,155 +1,155 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import collections
23 23
24 24 from pyramid.exceptions import ConfigurationError
25 25
26 26 from rhodecode.lib.utils2 import safe_str
27 27 from rhodecode.model.settings import SettingsModel
28 28 from rhodecode.translation import _
29 29
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 class AuthnResourceBase(object):
35 35 __name__ = None
36 36 __parent__ = None
37 37
38 38 def get_root(self):
39 39 current = self
40 40 while current.__parent__ is not None:
41 41 current = current.__parent__
42 42 return current
43 43
44 44
45 45 class AuthnPluginResourceBase(AuthnResourceBase):
46 46
47 47 def __init__(self, plugin):
48 48 self.plugin = plugin
49 49 self.__name__ = plugin.get_url_slug()
50 50 self.display_name = plugin.get_display_name()
51 51
52 52
53 53 class AuthnRootResource(AuthnResourceBase):
54 54 """
55 55 This is the root traversal resource object for the authentication settings.
56 56 """
57 57
58 58 def __init__(self):
59 59 self._store = collections.OrderedDict()
60 60 self._resource_name_map = {}
61 self.display_name = _('Global')
61 self.display_name = _('Authentication Plugins')
62 62
63 63 def __getitem__(self, key):
64 64 """
65 65 Customized get item function to return only items (plugins) that are
66 66 activated.
67 67 """
68 68 if self._is_item_active(key):
69 69 return self._store[key]
70 70 else:
71 71 raise KeyError('Authentication plugin "{}" is not active.'.format(
72 72 key))
73 73
74 74 def __iter__(self):
75 75 for key in self._store.keys():
76 76 if self._is_item_active(key):
77 77 yield self._store[key]
78 78
79 79 def _is_item_active(self, key):
80 80 activated_plugins = SettingsModel().get_auth_plugins()
81 81 plugin_id = self.get_plugin_id(key)
82 82 return plugin_id in activated_plugins
83 83
84 84 def get_plugin_id(self, resource_name):
85 85 """
86 86 Return the plugin id for the given traversal resource name.
87 87 """
88 88 # TODO: Store this info in the resource element.
89 89 return self._resource_name_map[resource_name]
90 90
91 91 def get_sorted_list(self):
92 92 """
93 93 Returns a sorted list of sub resources for displaying purposes.
94 94 """
95 95 def sort_key(resource):
96 96 return str.lower(safe_str(resource.display_name))
97 97
98 98 active = [item for item in self]
99 99 return sorted(active, key=sort_key)
100 100
101 101 def get_nav_list(self, sort=True):
102 102 """
103 103 Returns a sorted list of resources for displaying the navigation.
104 104 """
105 105 if sort:
106 106 nav_list = self.get_sorted_list()
107 107 else:
108 108 nav_list = [item for item in self]
109 109
110 110 nav_list.insert(0, self)
111 111 return nav_list
112 112
113 113 def add_authn_resource(self, config, plugin_id, resource):
114 114 """
115 115 Register a traversal resource as a sub element to the authentication
116 116 settings. This method is registered as a directive on the pyramid
117 117 configurator object and called by plugins.
118 118 """
119 119
120 120 def _ensure_unique_name(name, limit=100):
121 121 counter = 1
122 122 current = name
123 123 while current in self._store.keys():
124 124 current = '{}{}'.format(name, counter)
125 125 counter += 1
126 126 if counter > limit:
127 127 raise ConfigurationError(
128 128 'Cannot build unique name for traversal resource "%s" '
129 129 'registered by plugin "%s"', name, plugin_id)
130 130 return current
131 131
132 132 # Allow plugin resources with identical names by rename duplicates.
133 133 unique_name = _ensure_unique_name(resource.__name__)
134 134 if unique_name != resource.__name__:
135 135 log.warn('Name collision for traversal resource "%s" registered '
136 136 'by authentication plugin "%s"', resource.__name__,
137 137 plugin_id)
138 138 resource.__name__ = unique_name
139 139
140 140 log.debug('Register traversal resource "%s" for plugin "%s"',
141 141 unique_name, plugin_id)
142 142 self._resource_name_map[unique_name] = plugin_id
143 143 resource.__parent__ = self
144 144 self._store[unique_name] = resource
145 145
146 146
147 147 root = AuthnRootResource()
148 148
149 149
150 150 def root_factory(request=None):
151 151 """
152 152 Returns the root traversal resource instance used for the authentication
153 153 settings route.
154 154 """
155 155 return root
General Comments 0
You need to be logged in to leave comments. Login now