##// END OF EJS Templates
users: make AuthUser propert a method, and allow override of params.
marcink -
r1997:61825b68 default
parent child
Show More
@@ -1,129 +1,129
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.repo import RepoModel
24 from rhodecode.model.repo import RepoModel
25 from rhodecode.api.tests.utils import (
25 from rhodecode.api.tests.utils import (
26 build_data, api_call, assert_ok, assert_error, jsonify)
26 build_data, api_call, assert_ok, assert_error, jsonify)
27 from rhodecode.model.db import User
27 from rhodecode.model.db import User
28
28
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestGetRepos(object):
31 class TestGetRepos(object):
32 def test_api_get_repos(self):
32 def test_api_get_repos(self):
33 id_, params = build_data(self.apikey, 'get_repos')
33 id_, params = build_data(self.apikey, 'get_repos')
34 response = api_call(self.app, params)
34 response = api_call(self.app, params)
35
35
36 result = []
36 result = []
37 for repo in RepoModel().get_all():
37 for repo in RepoModel().get_all():
38 result.append(repo.get_api_data(include_secrets=True))
38 result.append(repo.get_api_data(include_secrets=True))
39 ret = jsonify(result)
39 ret = jsonify(result)
40
40
41 expected = ret
41 expected = ret
42 assert_ok(id_, expected, given=response.body)
42 assert_ok(id_, expected, given=response.body)
43
43
44 def test_api_get_repos_only_toplevel(self, user_util):
44 def test_api_get_repos_only_toplevel(self, user_util):
45 repo_group = user_util.create_repo_group(auto_cleanup=True)
45 repo_group = user_util.create_repo_group(auto_cleanup=True)
46 user_util.create_repo(parent=repo_group)
46 user_util.create_repo(parent=repo_group)
47
47
48 id_, params = build_data(self.apikey, 'get_repos', traverse=0)
48 id_, params = build_data(self.apikey, 'get_repos', traverse=0)
49 response = api_call(self.app, params)
49 response = api_call(self.app, params)
50
50
51 result = []
51 result = []
52 for repo in RepoModel().get_repos_for_root(root=None):
52 for repo in RepoModel().get_repos_for_root(root=None):
53 result.append(repo.get_api_data(include_secrets=True))
53 result.append(repo.get_api_data(include_secrets=True))
54 expected = jsonify(result)
54 expected = jsonify(result)
55
55
56 assert_ok(id_, expected, given=response.body)
56 assert_ok(id_, expected, given=response.body)
57
57
58 def test_api_get_repos_with_wrong_root(self):
58 def test_api_get_repos_with_wrong_root(self):
59 id_, params = build_data(self.apikey, 'get_repos', root='abracadabra')
59 id_, params = build_data(self.apikey, 'get_repos', root='abracadabra')
60 response = api_call(self.app, params)
60 response = api_call(self.app, params)
61
61
62 expected = 'Root repository group `abracadabra` does not exist'
62 expected = 'Root repository group `abracadabra` does not exist'
63 assert_error(id_, expected, given=response.body)
63 assert_error(id_, expected, given=response.body)
64
64
65 def test_api_get_repos_with_root(self, user_util):
65 def test_api_get_repos_with_root(self, user_util):
66 repo_group = user_util.create_repo_group(auto_cleanup=True)
66 repo_group = user_util.create_repo_group(auto_cleanup=True)
67 repo_group_name = repo_group.group_name
67 repo_group_name = repo_group.group_name
68
68
69 user_util.create_repo(parent=repo_group)
69 user_util.create_repo(parent=repo_group)
70 user_util.create_repo(parent=repo_group)
70 user_util.create_repo(parent=repo_group)
71
71
72 # nested, should not show up
72 # nested, should not show up
73 user_util._test_name = '{}/'.format(repo_group_name)
73 user_util._test_name = '{}/'.format(repo_group_name)
74 sub_repo_group = user_util.create_repo_group(auto_cleanup=True)
74 sub_repo_group = user_util.create_repo_group(auto_cleanup=True)
75 user_util.create_repo(parent=sub_repo_group)
75 user_util.create_repo(parent=sub_repo_group)
76
76
77 id_, params = build_data(self.apikey, 'get_repos',
77 id_, params = build_data(self.apikey, 'get_repos',
78 root=repo_group_name, traverse=0)
78 root=repo_group_name, traverse=0)
79 response = api_call(self.app, params)
79 response = api_call(self.app, params)
80
80
81 result = []
81 result = []
82 for repo in RepoModel().get_repos_for_root(repo_group):
82 for repo in RepoModel().get_repos_for_root(repo_group):
83 result.append(repo.get_api_data(include_secrets=True))
83 result.append(repo.get_api_data(include_secrets=True))
84
84
85 assert len(result) == 2
85 assert len(result) == 2
86 expected = jsonify(result)
86 expected = jsonify(result)
87 assert_ok(id_, expected, given=response.body)
87 assert_ok(id_, expected, given=response.body)
88
88
89 def test_api_get_repos_with_root_and_traverse(self, user_util):
89 def test_api_get_repos_with_root_and_traverse(self, user_util):
90 repo_group = user_util.create_repo_group(auto_cleanup=True)
90 repo_group = user_util.create_repo_group(auto_cleanup=True)
91 repo_group_name = repo_group.group_name
91 repo_group_name = repo_group.group_name
92
92
93 user_util.create_repo(parent=repo_group)
93 user_util.create_repo(parent=repo_group)
94 user_util.create_repo(parent=repo_group)
94 user_util.create_repo(parent=repo_group)
95
95
96 # nested, should not show up
96 # nested, should not show up
97 user_util._test_name = '{}/'.format(repo_group_name)
97 user_util._test_name = '{}/'.format(repo_group_name)
98 sub_repo_group = user_util.create_repo_group(auto_cleanup=True)
98 sub_repo_group = user_util.create_repo_group(auto_cleanup=True)
99 user_util.create_repo(parent=sub_repo_group)
99 user_util.create_repo(parent=sub_repo_group)
100
100
101 id_, params = build_data(self.apikey, 'get_repos',
101 id_, params = build_data(self.apikey, 'get_repos',
102 root=repo_group_name, traverse=1)
102 root=repo_group_name, traverse=1)
103 response = api_call(self.app, params)
103 response = api_call(self.app, params)
104
104
105 result = []
105 result = []
106 for repo in RepoModel().get_repos_for_root(
106 for repo in RepoModel().get_repos_for_root(
107 repo_group_name, traverse=True):
107 repo_group_name, traverse=True):
108 result.append(repo.get_api_data(include_secrets=True))
108 result.append(repo.get_api_data(include_secrets=True))
109
109
110 assert len(result) == 3
110 assert len(result) == 3
111 expected = jsonify(result)
111 expected = jsonify(result)
112 assert_ok(id_, expected, given=response.body)
112 assert_ok(id_, expected, given=response.body)
113
113
114 def test_api_get_repos_non_admin(self):
114 def test_api_get_repos_non_admin(self):
115 id_, params = build_data(self.apikey_regular, 'get_repos')
115 id_, params = build_data(self.apikey_regular, 'get_repos')
116 response = api_call(self.app, params)
116 response = api_call(self.app, params)
117
117
118 user = User.get_by_username(self.TEST_USER_LOGIN)
118 user = User.get_by_username(self.TEST_USER_LOGIN)
119 allowed_repos = user.AuthUser.permissions['repositories']
119 allowed_repos = user.AuthUser().permissions['repositories']
120
120
121 result = []
121 result = []
122 for repo in RepoModel().get_all():
122 for repo in RepoModel().get_all():
123 perm = allowed_repos[repo.repo_name]
123 perm = allowed_repos[repo.repo_name]
124 if perm in ['repository.read', 'repository.write', 'repository.admin']:
124 if perm in ['repository.read', 'repository.write', 'repository.admin']:
125 result.append(repo.get_api_data())
125 result.append(repo.get_api_data())
126 ret = jsonify(result)
126 ret = jsonify(result)
127
127
128 expected = ret
128 expected = ret
129 assert_ok(id_, expected, given=response.body)
129 assert_ok(id_, expected, given=response.body)
@@ -1,369 +1,369
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23 import formencode
23 import formencode
24 from pyramid.interfaces import IRoutesMapper
24 from pyramid.interfaces import IRoutesMapper
25
25
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.httpexceptions import HTTPFound
27 from pyramid.httpexceptions import HTTPFound
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView
31 from rhodecode.apps._base import BaseAppView
32
32
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import (
35 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
35 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
36 from rhodecode.lib.utils2 import aslist
36 from rhodecode.lib.utils2 import aslist
37 from rhodecode.model.db import User, UserIpMap
37 from rhodecode.model.db import User, UserIpMap
38 from rhodecode.model.forms import (
38 from rhodecode.model.forms import (
39 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
39 ApplicationPermissionsForm, ObjectPermissionsForm, UserPermissionsForm)
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.settings import SettingsModel
43
43
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class AdminPermissionsView(BaseAppView):
48 class AdminPermissionsView(BaseAppView):
49 def load_default_context(self):
49 def load_default_context(self):
50 c = self._get_local_tmpl_context()
50 c = self._get_local_tmpl_context()
51
51
52 self._register_global_c(c)
52 self._register_global_c(c)
53 PermissionModel().set_global_permission_choices(
53 PermissionModel().set_global_permission_choices(
54 c, gettext_translator=self.request.translate)
54 c, gettext_translator=self.request.translate)
55 return c
55 return c
56
56
57 @LoginRequired()
57 @LoginRequired()
58 @HasPermissionAllDecorator('hg.admin')
58 @HasPermissionAllDecorator('hg.admin')
59 @view_config(
59 @view_config(
60 route_name='admin_permissions_application', request_method='GET',
60 route_name='admin_permissions_application', request_method='GET',
61 renderer='rhodecode:templates/admin/permissions/permissions.mako')
61 renderer='rhodecode:templates/admin/permissions/permissions.mako')
62 def permissions_application(self):
62 def permissions_application(self):
63 c = self.load_default_context()
63 c = self.load_default_context()
64 c.active = 'application'
64 c.active = 'application'
65
65
66 c.user = User.get_default_user(refresh=True)
66 c.user = User.get_default_user(refresh=True)
67
67
68 app_settings = SettingsModel().get_all_settings()
68 app_settings = SettingsModel().get_all_settings()
69 defaults = {
69 defaults = {
70 'anonymous': c.user.active,
70 'anonymous': c.user.active,
71 'default_register_message': app_settings.get(
71 'default_register_message': app_settings.get(
72 'rhodecode_register_message')
72 'rhodecode_register_message')
73 }
73 }
74 defaults.update(c.user.get_default_perms())
74 defaults.update(c.user.get_default_perms())
75
75
76 data = render('rhodecode:templates/admin/permissions/permissions.mako',
76 data = render('rhodecode:templates/admin/permissions/permissions.mako',
77 self._get_template_context(c), self.request)
77 self._get_template_context(c), self.request)
78 html = formencode.htmlfill.render(
78 html = formencode.htmlfill.render(
79 data,
79 data,
80 defaults=defaults,
80 defaults=defaults,
81 encoding="UTF-8",
81 encoding="UTF-8",
82 force_defaults=False
82 force_defaults=False
83 )
83 )
84 return Response(html)
84 return Response(html)
85
85
86 @LoginRequired()
86 @LoginRequired()
87 @HasPermissionAllDecorator('hg.admin')
87 @HasPermissionAllDecorator('hg.admin')
88 @CSRFRequired()
88 @CSRFRequired()
89 @view_config(
89 @view_config(
90 route_name='admin_permissions_application_update', request_method='POST',
90 route_name='admin_permissions_application_update', request_method='POST',
91 renderer='rhodecode:templates/admin/permissions/permissions.mako')
91 renderer='rhodecode:templates/admin/permissions/permissions.mako')
92 def permissions_application_update(self):
92 def permissions_application_update(self):
93 _ = self.request.translate
93 _ = self.request.translate
94 c = self.load_default_context()
94 c = self.load_default_context()
95 c.active = 'application'
95 c.active = 'application'
96
96
97 _form = ApplicationPermissionsForm(
97 _form = ApplicationPermissionsForm(
98 [x[0] for x in c.register_choices],
98 [x[0] for x in c.register_choices],
99 [x[0] for x in c.password_reset_choices],
99 [x[0] for x in c.password_reset_choices],
100 [x[0] for x in c.extern_activate_choices])()
100 [x[0] for x in c.extern_activate_choices])()
101
101
102 try:
102 try:
103 form_result = _form.to_python(dict(self.request.POST))
103 form_result = _form.to_python(dict(self.request.POST))
104 form_result.update({'perm_user_name': User.DEFAULT_USER})
104 form_result.update({'perm_user_name': User.DEFAULT_USER})
105 PermissionModel().update_application_permissions(form_result)
105 PermissionModel().update_application_permissions(form_result)
106
106
107 settings = [
107 settings = [
108 ('register_message', 'default_register_message'),
108 ('register_message', 'default_register_message'),
109 ]
109 ]
110 for setting, form_key in settings:
110 for setting, form_key in settings:
111 sett = SettingsModel().create_or_update_setting(
111 sett = SettingsModel().create_or_update_setting(
112 setting, form_result[form_key])
112 setting, form_result[form_key])
113 Session().add(sett)
113 Session().add(sett)
114
114
115 Session().commit()
115 Session().commit()
116 h.flash(_('Application permissions updated successfully'),
116 h.flash(_('Application permissions updated successfully'),
117 category='success')
117 category='success')
118
118
119 except formencode.Invalid as errors:
119 except formencode.Invalid as errors:
120 defaults = errors.value
120 defaults = errors.value
121
121
122 data = render(
122 data = render(
123 'rhodecode:templates/admin/permissions/permissions.mako',
123 'rhodecode:templates/admin/permissions/permissions.mako',
124 self._get_template_context(c), self.request)
124 self._get_template_context(c), self.request)
125 html = formencode.htmlfill.render(
125 html = formencode.htmlfill.render(
126 data,
126 data,
127 defaults=defaults,
127 defaults=defaults,
128 errors=errors.error_dict or {},
128 errors=errors.error_dict or {},
129 prefix_error=False,
129 prefix_error=False,
130 encoding="UTF-8",
130 encoding="UTF-8",
131 force_defaults=False
131 force_defaults=False
132 )
132 )
133 return Response(html)
133 return Response(html)
134
134
135 except Exception:
135 except Exception:
136 log.exception("Exception during update of permissions")
136 log.exception("Exception during update of permissions")
137 h.flash(_('Error occurred during update of permissions'),
137 h.flash(_('Error occurred during update of permissions'),
138 category='error')
138 category='error')
139
139
140 raise HTTPFound(h.route_path('admin_permissions_application'))
140 raise HTTPFound(h.route_path('admin_permissions_application'))
141
141
142 @LoginRequired()
142 @LoginRequired()
143 @HasPermissionAllDecorator('hg.admin')
143 @HasPermissionAllDecorator('hg.admin')
144 @view_config(
144 @view_config(
145 route_name='admin_permissions_object', request_method='GET',
145 route_name='admin_permissions_object', request_method='GET',
146 renderer='rhodecode:templates/admin/permissions/permissions.mako')
146 renderer='rhodecode:templates/admin/permissions/permissions.mako')
147 def permissions_objects(self):
147 def permissions_objects(self):
148 c = self.load_default_context()
148 c = self.load_default_context()
149 c.active = 'objects'
149 c.active = 'objects'
150
150
151 c.user = User.get_default_user(refresh=True)
151 c.user = User.get_default_user(refresh=True)
152 defaults = {}
152 defaults = {}
153 defaults.update(c.user.get_default_perms())
153 defaults.update(c.user.get_default_perms())
154
154
155 data = render(
155 data = render(
156 'rhodecode:templates/admin/permissions/permissions.mako',
156 'rhodecode:templates/admin/permissions/permissions.mako',
157 self._get_template_context(c), self.request)
157 self._get_template_context(c), self.request)
158 html = formencode.htmlfill.render(
158 html = formencode.htmlfill.render(
159 data,
159 data,
160 defaults=defaults,
160 defaults=defaults,
161 encoding="UTF-8",
161 encoding="UTF-8",
162 force_defaults=False
162 force_defaults=False
163 )
163 )
164 return Response(html)
164 return Response(html)
165
165
166 @LoginRequired()
166 @LoginRequired()
167 @HasPermissionAllDecorator('hg.admin')
167 @HasPermissionAllDecorator('hg.admin')
168 @CSRFRequired()
168 @CSRFRequired()
169 @view_config(
169 @view_config(
170 route_name='admin_permissions_object_update', request_method='POST',
170 route_name='admin_permissions_object_update', request_method='POST',
171 renderer='rhodecode:templates/admin/permissions/permissions.mako')
171 renderer='rhodecode:templates/admin/permissions/permissions.mako')
172 def permissions_objects_update(self):
172 def permissions_objects_update(self):
173 _ = self.request.translate
173 _ = self.request.translate
174 c = self.load_default_context()
174 c = self.load_default_context()
175 c.active = 'objects'
175 c.active = 'objects'
176
176
177 _form = ObjectPermissionsForm(
177 _form = ObjectPermissionsForm(
178 [x[0] for x in c.repo_perms_choices],
178 [x[0] for x in c.repo_perms_choices],
179 [x[0] for x in c.group_perms_choices],
179 [x[0] for x in c.group_perms_choices],
180 [x[0] for x in c.user_group_perms_choices])()
180 [x[0] for x in c.user_group_perms_choices])()
181
181
182 try:
182 try:
183 form_result = _form.to_python(dict(self.request.POST))
183 form_result = _form.to_python(dict(self.request.POST))
184 form_result.update({'perm_user_name': User.DEFAULT_USER})
184 form_result.update({'perm_user_name': User.DEFAULT_USER})
185 PermissionModel().update_object_permissions(form_result)
185 PermissionModel().update_object_permissions(form_result)
186
186
187 Session().commit()
187 Session().commit()
188 h.flash(_('Object permissions updated successfully'),
188 h.flash(_('Object permissions updated successfully'),
189 category='success')
189 category='success')
190
190
191 except formencode.Invalid as errors:
191 except formencode.Invalid as errors:
192 defaults = errors.value
192 defaults = errors.value
193
193
194 data = render(
194 data = render(
195 'rhodecode:templates/admin/permissions/permissions.mako',
195 'rhodecode:templates/admin/permissions/permissions.mako',
196 self._get_template_context(c), self.request)
196 self._get_template_context(c), self.request)
197 html = formencode.htmlfill.render(
197 html = formencode.htmlfill.render(
198 data,
198 data,
199 defaults=defaults,
199 defaults=defaults,
200 errors=errors.error_dict or {},
200 errors=errors.error_dict or {},
201 prefix_error=False,
201 prefix_error=False,
202 encoding="UTF-8",
202 encoding="UTF-8",
203 force_defaults=False
203 force_defaults=False
204 )
204 )
205 return Response(html)
205 return Response(html)
206 except Exception:
206 except Exception:
207 log.exception("Exception during update of permissions")
207 log.exception("Exception during update of permissions")
208 h.flash(_('Error occurred during update of permissions'),
208 h.flash(_('Error occurred during update of permissions'),
209 category='error')
209 category='error')
210
210
211 raise HTTPFound(h.route_path('admin_permissions_object'))
211 raise HTTPFound(h.route_path('admin_permissions_object'))
212
212
213 @LoginRequired()
213 @LoginRequired()
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 @view_config(
215 @view_config(
216 route_name='admin_permissions_global', request_method='GET',
216 route_name='admin_permissions_global', request_method='GET',
217 renderer='rhodecode:templates/admin/permissions/permissions.mako')
217 renderer='rhodecode:templates/admin/permissions/permissions.mako')
218 def permissions_global(self):
218 def permissions_global(self):
219 c = self.load_default_context()
219 c = self.load_default_context()
220 c.active = 'global'
220 c.active = 'global'
221
221
222 c.user = User.get_default_user(refresh=True)
222 c.user = User.get_default_user(refresh=True)
223 defaults = {}
223 defaults = {}
224 defaults.update(c.user.get_default_perms())
224 defaults.update(c.user.get_default_perms())
225
225
226 data = render(
226 data = render(
227 'rhodecode:templates/admin/permissions/permissions.mako',
227 'rhodecode:templates/admin/permissions/permissions.mako',
228 self._get_template_context(c), self.request)
228 self._get_template_context(c), self.request)
229 html = formencode.htmlfill.render(
229 html = formencode.htmlfill.render(
230 data,
230 data,
231 defaults=defaults,
231 defaults=defaults,
232 encoding="UTF-8",
232 encoding="UTF-8",
233 force_defaults=False
233 force_defaults=False
234 )
234 )
235 return Response(html)
235 return Response(html)
236
236
237 @LoginRequired()
237 @LoginRequired()
238 @HasPermissionAllDecorator('hg.admin')
238 @HasPermissionAllDecorator('hg.admin')
239 @CSRFRequired()
239 @CSRFRequired()
240 @view_config(
240 @view_config(
241 route_name='admin_permissions_global_update', request_method='POST',
241 route_name='admin_permissions_global_update', request_method='POST',
242 renderer='rhodecode:templates/admin/permissions/permissions.mako')
242 renderer='rhodecode:templates/admin/permissions/permissions.mako')
243 def permissions_global_update(self):
243 def permissions_global_update(self):
244 _ = self.request.translate
244 _ = self.request.translate
245 c = self.load_default_context()
245 c = self.load_default_context()
246 c.active = 'global'
246 c.active = 'global'
247
247
248 _form = UserPermissionsForm(
248 _form = UserPermissionsForm(
249 [x[0] for x in c.repo_create_choices],
249 [x[0] for x in c.repo_create_choices],
250 [x[0] for x in c.repo_create_on_write_choices],
250 [x[0] for x in c.repo_create_on_write_choices],
251 [x[0] for x in c.repo_group_create_choices],
251 [x[0] for x in c.repo_group_create_choices],
252 [x[0] for x in c.user_group_create_choices],
252 [x[0] for x in c.user_group_create_choices],
253 [x[0] for x in c.fork_choices],
253 [x[0] for x in c.fork_choices],
254 [x[0] for x in c.inherit_default_permission_choices])()
254 [x[0] for x in c.inherit_default_permission_choices])()
255
255
256 try:
256 try:
257 form_result = _form.to_python(dict(self.request.POST))
257 form_result = _form.to_python(dict(self.request.POST))
258 form_result.update({'perm_user_name': User.DEFAULT_USER})
258 form_result.update({'perm_user_name': User.DEFAULT_USER})
259 PermissionModel().update_user_permissions(form_result)
259 PermissionModel().update_user_permissions(form_result)
260
260
261 Session().commit()
261 Session().commit()
262 h.flash(_('Global permissions updated successfully'),
262 h.flash(_('Global permissions updated successfully'),
263 category='success')
263 category='success')
264
264
265 except formencode.Invalid as errors:
265 except formencode.Invalid as errors:
266 defaults = errors.value
266 defaults = errors.value
267
267
268 data = render(
268 data = render(
269 'rhodecode:templates/admin/permissions/permissions.mako',
269 'rhodecode:templates/admin/permissions/permissions.mako',
270 self._get_template_context(c), self.request)
270 self._get_template_context(c), self.request)
271 html = formencode.htmlfill.render(
271 html = formencode.htmlfill.render(
272 data,
272 data,
273 defaults=defaults,
273 defaults=defaults,
274 errors=errors.error_dict or {},
274 errors=errors.error_dict or {},
275 prefix_error=False,
275 prefix_error=False,
276 encoding="UTF-8",
276 encoding="UTF-8",
277 force_defaults=False
277 force_defaults=False
278 )
278 )
279 return Response(html)
279 return Response(html)
280 except Exception:
280 except Exception:
281 log.exception("Exception during update of permissions")
281 log.exception("Exception during update of permissions")
282 h.flash(_('Error occurred during update of permissions'),
282 h.flash(_('Error occurred during update of permissions'),
283 category='error')
283 category='error')
284
284
285 raise HTTPFound(h.route_path('admin_permissions_global'))
285 raise HTTPFound(h.route_path('admin_permissions_global'))
286
286
287 @LoginRequired()
287 @LoginRequired()
288 @HasPermissionAllDecorator('hg.admin')
288 @HasPermissionAllDecorator('hg.admin')
289 @view_config(
289 @view_config(
290 route_name='admin_permissions_ips', request_method='GET',
290 route_name='admin_permissions_ips', request_method='GET',
291 renderer='rhodecode:templates/admin/permissions/permissions.mako')
291 renderer='rhodecode:templates/admin/permissions/permissions.mako')
292 def permissions_ips(self):
292 def permissions_ips(self):
293 c = self.load_default_context()
293 c = self.load_default_context()
294 c.active = 'ips'
294 c.active = 'ips'
295
295
296 c.user = User.get_default_user(refresh=True)
296 c.user = User.get_default_user(refresh=True)
297 c.user_ip_map = (
297 c.user_ip_map = (
298 UserIpMap.query().filter(UserIpMap.user == c.user).all())
298 UserIpMap.query().filter(UserIpMap.user == c.user).all())
299
299
300 return self._get_template_context(c)
300 return self._get_template_context(c)
301
301
302 @LoginRequired()
302 @LoginRequired()
303 @HasPermissionAllDecorator('hg.admin')
303 @HasPermissionAllDecorator('hg.admin')
304 @view_config(
304 @view_config(
305 route_name='admin_permissions_overview', request_method='GET',
305 route_name='admin_permissions_overview', request_method='GET',
306 renderer='rhodecode:templates/admin/permissions/permissions.mako')
306 renderer='rhodecode:templates/admin/permissions/permissions.mako')
307 def permissions_overview(self):
307 def permissions_overview(self):
308 c = self.load_default_context()
308 c = self.load_default_context()
309 c.active = 'perms'
309 c.active = 'perms'
310
310
311 c.user = User.get_default_user(refresh=True)
311 c.user = User.get_default_user(refresh=True)
312 c.perm_user = c.user.AuthUser
312 c.perm_user = c.user.AuthUser()
313 return self._get_template_context(c)
313 return self._get_template_context(c)
314
314
315 @LoginRequired()
315 @LoginRequired()
316 @HasPermissionAllDecorator('hg.admin')
316 @HasPermissionAllDecorator('hg.admin')
317 @view_config(
317 @view_config(
318 route_name='admin_permissions_auth_token_access', request_method='GET',
318 route_name='admin_permissions_auth_token_access', request_method='GET',
319 renderer='rhodecode:templates/admin/permissions/permissions.mako')
319 renderer='rhodecode:templates/admin/permissions/permissions.mako')
320 def auth_token_access(self):
320 def auth_token_access(self):
321 from rhodecode import CONFIG
321 from rhodecode import CONFIG
322
322
323 c = self.load_default_context()
323 c = self.load_default_context()
324 c.active = 'auth_token_access'
324 c.active = 'auth_token_access'
325
325
326 c.user = User.get_default_user(refresh=True)
326 c.user = User.get_default_user(refresh=True)
327 c.perm_user = c.user.AuthUser
327 c.perm_user = c.user.AuthUser()
328
328
329 mapper = self.request.registry.queryUtility(IRoutesMapper)
329 mapper = self.request.registry.queryUtility(IRoutesMapper)
330 c.view_data = []
330 c.view_data = []
331
331
332 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
332 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
333 introspector = self.request.registry.introspector
333 introspector = self.request.registry.introspector
334
334
335 view_intr = {}
335 view_intr = {}
336 for view_data in introspector.get_category('views'):
336 for view_data in introspector.get_category('views'):
337 intr = view_data['introspectable']
337 intr = view_data['introspectable']
338
338
339 if 'route_name' in intr and intr['attr']:
339 if 'route_name' in intr and intr['attr']:
340 view_intr[intr['route_name']] = '{}:{}'.format(
340 view_intr[intr['route_name']] = '{}:{}'.format(
341 str(intr['derived_callable'].func_name), intr['attr']
341 str(intr['derived_callable'].func_name), intr['attr']
342 )
342 )
343
343
344 c.whitelist_key = 'api_access_controllers_whitelist'
344 c.whitelist_key = 'api_access_controllers_whitelist'
345 c.whitelist_file = CONFIG.get('__file__')
345 c.whitelist_file = CONFIG.get('__file__')
346 whitelist_views = aslist(
346 whitelist_views = aslist(
347 CONFIG.get(c.whitelist_key), sep=',')
347 CONFIG.get(c.whitelist_key), sep=',')
348
348
349 for route_info in mapper.get_routes():
349 for route_info in mapper.get_routes():
350 if not route_info.name.startswith('__'):
350 if not route_info.name.startswith('__'):
351 routepath = route_info.pattern
351 routepath = route_info.pattern
352
352
353 def replace(matchobj):
353 def replace(matchobj):
354 if matchobj.group(1):
354 if matchobj.group(1):
355 return "{%s}" % matchobj.group(1).split(':')[0]
355 return "{%s}" % matchobj.group(1).split(':')[0]
356 else:
356 else:
357 return "{%s}" % matchobj.group(2)
357 return "{%s}" % matchobj.group(2)
358
358
359 routepath = _argument_prog.sub(replace, routepath)
359 routepath = _argument_prog.sub(replace, routepath)
360
360
361 if not routepath.startswith('/'):
361 if not routepath.startswith('/'):
362 routepath = '/' + routepath
362 routepath = '/' + routepath
363
363
364 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
364 view_fqn = view_intr.get(route_info.name, 'NOT AVAILABLE')
365 active = view_fqn in whitelist_views
365 active = view_fqn in whitelist_views
366 c.view_data.append((route_info.name, view_fqn, routepath, active))
366 c.view_data.append((route_info.name, view_fqn, routepath, active))
367
367
368 c.whitelist_views = whitelist_views
368 c.whitelist_views = whitelist_views
369 return self._get_template_context(c)
369 return self._get_template_context(c)
@@ -1,133 +1,133
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import mock
22 import mock
23 import pytest
23 import pytest
24
24
25 from rhodecode.apps.login.views import LoginView, CaptchaData
25 from rhodecode.apps.login.views import LoginView, CaptchaData
26 from rhodecode.config.routing import ADMIN_PREFIX
26 from rhodecode.config.routing import ADMIN_PREFIX
27 from rhodecode.lib.utils2 import AttributeDict
27 from rhodecode.lib.utils2 import AttributeDict
28 from rhodecode.model.settings import SettingsModel
28 from rhodecode.model.settings import SettingsModel
29 from rhodecode.tests.utils import AssertResponse
29 from rhodecode.tests.utils import AssertResponse
30
30
31
31
32 class RhodeCodeSetting(object):
32 class RhodeCodeSetting(object):
33 def __init__(self, name, value):
33 def __init__(self, name, value):
34 self.name = name
34 self.name = name
35 self.value = value
35 self.value = value
36
36
37 def __enter__(self):
37 def __enter__(self):
38 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.settings import SettingsModel
39 model = SettingsModel()
39 model = SettingsModel()
40 self.old_setting = model.get_setting_by_name(self.name)
40 self.old_setting = model.get_setting_by_name(self.name)
41 model.create_or_update_setting(name=self.name, val=self.value)
41 model.create_or_update_setting(name=self.name, val=self.value)
42 return self
42 return self
43
43
44 def __exit__(self, exc_type, exc_val, exc_tb):
44 def __exit__(self, exc_type, exc_val, exc_tb):
45 model = SettingsModel()
45 model = SettingsModel()
46 if self.old_setting:
46 if self.old_setting:
47 model.create_or_update_setting(
47 model.create_or_update_setting(
48 name=self.name, val=self.old_setting.app_settings_value)
48 name=self.name, val=self.old_setting.app_settings_value)
49 else:
49 else:
50 model.create_or_update_setting(name=self.name)
50 model.create_or_update_setting(name=self.name)
51
51
52
52
53 class TestRegisterCaptcha(object):
53 class TestRegisterCaptcha(object):
54
54
55 @pytest.mark.parametrize('private_key, public_key, expected', [
55 @pytest.mark.parametrize('private_key, public_key, expected', [
56 ('', '', CaptchaData(False, '', '')),
56 ('', '', CaptchaData(False, '', '')),
57 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
57 ('', 'pubkey', CaptchaData(False, '', 'pubkey')),
58 ('privkey', '', CaptchaData(True, 'privkey', '')),
58 ('privkey', '', CaptchaData(True, 'privkey', '')),
59 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
59 ('privkey', 'pubkey', CaptchaData(True, 'privkey', 'pubkey')),
60 ])
60 ])
61 def test_get_captcha_data(self, private_key, public_key, expected, db,
61 def test_get_captcha_data(self, private_key, public_key, expected, db,
62 request_stub, user_util):
62 request_stub, user_util):
63 request_stub.user = user_util.create_user().AuthUser
63 request_stub.user = user_util.create_user().AuthUser()
64 request_stub.matched_route = AttributeDict({'name': 'login'})
64 request_stub.matched_route = AttributeDict({'name': 'login'})
65 login_view = LoginView(mock.Mock(), request_stub)
65 login_view = LoginView(mock.Mock(), request_stub)
66
66
67 with RhodeCodeSetting('captcha_private_key', private_key):
67 with RhodeCodeSetting('captcha_private_key', private_key):
68 with RhodeCodeSetting('captcha_public_key', public_key):
68 with RhodeCodeSetting('captcha_public_key', public_key):
69 captcha = login_view._get_captcha_data()
69 captcha = login_view._get_captcha_data()
70 assert captcha == expected
70 assert captcha == expected
71
71
72 @pytest.mark.parametrize('active', [False, True])
72 @pytest.mark.parametrize('active', [False, True])
73 @mock.patch.object(LoginView, '_get_captcha_data')
73 @mock.patch.object(LoginView, '_get_captcha_data')
74 def test_private_key_does_not_leak_to_html(
74 def test_private_key_does_not_leak_to_html(
75 self, m_get_captcha_data, active, app):
75 self, m_get_captcha_data, active, app):
76 captcha = CaptchaData(
76 captcha = CaptchaData(
77 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
77 active=active, private_key='PRIVATE_KEY', public_key='PUBLIC_KEY')
78 m_get_captcha_data.return_value = captcha
78 m_get_captcha_data.return_value = captcha
79
79
80 response = app.get(ADMIN_PREFIX + '/register')
80 response = app.get(ADMIN_PREFIX + '/register')
81 assert 'PRIVATE_KEY' not in response
81 assert 'PRIVATE_KEY' not in response
82
82
83 @pytest.mark.parametrize('active', [False, True])
83 @pytest.mark.parametrize('active', [False, True])
84 @mock.patch.object(LoginView, '_get_captcha_data')
84 @mock.patch.object(LoginView, '_get_captcha_data')
85 def test_register_view_renders_captcha(
85 def test_register_view_renders_captcha(
86 self, m_get_captcha_data, active, app):