##// END OF EJS Templates
users-admin: moved views into pyramid for editing emails and ips....
marcink -
r1821:a4cc42be default
parent child Browse files
Show More
@@ -1,115 +1,142 b''
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 from rhodecode.apps.admin.navigation import NavigationRegistry
22 from rhodecode.apps.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def admin_routes(config):
27 def admin_routes(config):
28 """
28 """
29 Admin prefixed routes
29 Admin prefixed routes
30 """
30 """
31
31
32 config.add_route(
32 config.add_route(
33 name='admin_audit_logs',
33 name='admin_audit_logs',
34 pattern='/audit_logs')
34 pattern='/audit_logs')
35
35
36 config.add_route(
36 config.add_route(
37 name='pull_requests_global_0', # backward compat
37 name='pull_requests_global_0', # backward compat
38 pattern='/pull_requests/{pull_request_id:[0-9]+}')
38 pattern='/pull_requests/{pull_request_id:[0-9]+}')
39 config.add_route(
39 config.add_route(
40 name='pull_requests_global_1', # backward compat
40 name='pull_requests_global_1', # backward compat
41 pattern='/pull-requests/{pull_request_id:[0-9]+}')
41 pattern='/pull-requests/{pull_request_id:[0-9]+}')
42 config.add_route(
42 config.add_route(
43 name='pull_requests_global',
43 name='pull_requests_global',
44 pattern='/pull-request/{pull_request_id:[0-9]+}')
44 pattern='/pull-request/{pull_request_id:[0-9]+}')
45
45
46 config.add_route(
46 config.add_route(
47 name='admin_settings_open_source',
47 name='admin_settings_open_source',
48 pattern='/settings/open_source')
48 pattern='/settings/open_source')
49 config.add_route(
49 config.add_route(
50 name='admin_settings_vcs_svn_generate_cfg',
50 name='admin_settings_vcs_svn_generate_cfg',
51 pattern='/settings/vcs/svn_generate_cfg')
51 pattern='/settings/vcs/svn_generate_cfg')
52
52
53 config.add_route(
53 config.add_route(
54 name='admin_settings_system',
54 name='admin_settings_system',
55 pattern='/settings/system')
55 pattern='/settings/system')
56 config.add_route(
56 config.add_route(
57 name='admin_settings_system_update',
57 name='admin_settings_system_update',
58 pattern='/settings/system/updates')
58 pattern='/settings/system/updates')
59
59
60 config.add_route(
60 config.add_route(
61 name='admin_settings_sessions',
61 name='admin_settings_sessions',
62 pattern='/settings/sessions')
62 pattern='/settings/sessions')
63 config.add_route(
63 config.add_route(
64 name='admin_settings_sessions_cleanup',
64 name='admin_settings_sessions_cleanup',
65 pattern='/settings/sessions/cleanup')
65 pattern='/settings/sessions/cleanup')
66
66
67 # global permissions
68 config.add_route(
69 name='admin_permissions_ips',
70 pattern='/permissions/ips')
71
67 # users admin
72 # users admin
68 config.add_route(
73 config.add_route(
69 name='users',
74 name='users',
70 pattern='/users')
75 pattern='/users')
71
76
72 config.add_route(
77 config.add_route(
73 name='users_data',
78 name='users_data',
74 pattern='/users_data')
79 pattern='/users_data')
75
80
76 # user auth tokens
81 # user auth tokens
77 config.add_route(
82 config.add_route(
78 name='edit_user_auth_tokens',
83 name='edit_user_auth_tokens',
79 pattern='/users/{user_id:\d+}/edit/auth_tokens')
84 pattern='/users/{user_id:\d+}/edit/auth_tokens')
80 config.add_route(
85 config.add_route(
81 name='edit_user_auth_tokens_add',
86 name='edit_user_auth_tokens_add',
82 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
87 pattern='/users/{user_id:\d+}/edit/auth_tokens/new')
83 config.add_route(
88 config.add_route(
84 name='edit_user_auth_tokens_delete',
89 name='edit_user_auth_tokens_delete',
85 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
90 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete')
86
91
92 # user emails
93 config.add_route(
94 name='edit_user_emails',
95 pattern='/users/{user_id:\d+}/edit/emails')
96 config.add_route(
97 name='edit_user_emails_add',
98 pattern='/users/{user_id:\d+}/edit/emails/new')
99 config.add_route(
100 name='edit_user_emails_delete',
101 pattern='/users/{user_id:\d+}/edit/emails/delete')
102
103 # user IPs
104 config.add_route(
105 name='edit_user_ips',
106 pattern='/users/{user_id:\d+}/edit/ips')
107 config.add_route(
108 name='edit_user_ips_add',
109 pattern='/users/{user_id:\d+}/edit/ips/new')
110 config.add_route(
111 name='edit_user_ips_delete',
112 pattern='/users/{user_id:\d+}/edit/ips/delete')
113
87 # user groups management
114 # user groups management
88 config.add_route(
115 config.add_route(
89 name='edit_user_groups_management',
116 name='edit_user_groups_management',
90 pattern='/users/{user_id:\d+}/edit/groups_management')
117 pattern='/users/{user_id:\d+}/edit/groups_management')
91
118
92 config.add_route(
119 config.add_route(
93 name='edit_user_groups_management_updates',
120 name='edit_user_groups_management_updates',
94 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
121 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates')
95
122
96 # user audit logs
123 # user audit logs
97 config.add_route(
124 config.add_route(
98 name='edit_user_audit_logs',
125 name='edit_user_audit_logs',
99 pattern='/users/{user_id:\d+}/edit/audit')
126 pattern='/users/{user_id:\d+}/edit/audit')
100
127
101
128
102 def includeme(config):
129 def includeme(config):
103 settings = config.get_settings()
130 settings = config.get_settings()
104
131
105 # Create admin navigation registry and add it to the pyramid registry.
132 # Create admin navigation registry and add it to the pyramid registry.
106 labs_active = str2bool(settings.get('labs_settings_active', False))
133 labs_active = str2bool(settings.get('labs_settings_active', False))
107 navigation_registry = NavigationRegistry(labs_active=labs_active)
134 navigation_registry = NavigationRegistry(labs_active=labs_active)
108 config.registry.registerUtility(navigation_registry)
135 config.registry.registerUtility(navigation_registry)
109
136
110 # main admin routes
137 # main admin routes
111 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
138 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
112 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
139 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
113
140
114 # Scan module for configuration decorators.
141 # Scan module for configuration decorators.
115 config.scan()
142 config.scan()
@@ -1,142 +1,281 b''
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 import pytest
21 import pytest
22
22
23 from rhodecode.model.db import User, UserApiKeys
23 from rhodecode.model.db import User, UserApiKeys, UserEmailMap
24 from rhodecode.model.meta import Session
25 from rhodecode.model.user import UserModel
24
26
25 from rhodecode.tests import (
27 from rhodecode.tests import (
26 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
28 TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash)
27 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.fixture import Fixture
28
30
29 fixture = Fixture()
31 fixture = Fixture()
30
32
31
33
32 def route_path(name, params=None, **kwargs):
34 def route_path(name, params=None, **kwargs):
33 import urllib
35 import urllib
34 from rhodecode.apps._base import ADMIN_PREFIX
36 from rhodecode.apps._base import ADMIN_PREFIX
35
37
36 base_url = {
38 base_url = {
37 'users':
39 'users':
38 ADMIN_PREFIX + '/users',
40 ADMIN_PREFIX + '/users',
39 'users_data':
41 'users_data':
40 ADMIN_PREFIX + '/users_data',
42 ADMIN_PREFIX + '/users_data',
41 'edit_user_auth_tokens':
43 'edit_user_auth_tokens':
42 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
44 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens',
43 'edit_user_auth_tokens_add':
45 'edit_user_auth_tokens_add':
44 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
46 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/new',
45 'edit_user_auth_tokens_delete':
47 'edit_user_auth_tokens_delete':
46 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
48 ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete',
49
50 'edit_user_emails':
51 ADMIN_PREFIX + '/users/{user_id}/edit/emails',
52 'edit_user_emails_add':
53 ADMIN_PREFIX + '/users/{user_id}/edit/emails/new',
54 'edit_user_emails_delete':
55 ADMIN_PREFIX + '/users/{user_id}/edit/emails/delete',
56
57 'edit_user_ips':
58 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
59 'edit_user_ips_add':
60 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
61 'edit_user_ips_delete':
62 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
47 }[name].format(**kwargs)
63 }[name].format(**kwargs)
48
64
49 if params:
65 if params:
50 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
66 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
51 return base_url
67 return base_url
52
68
53
69
54 class TestAdminUsersView(TestController):
70 class TestAdminUsersView(TestController):
55
71
56 def test_show_users(self):
72 def test_show_users(self):
57 self.log_user()
73 self.log_user()
58 self.app.get(route_path('users'))
74 self.app.get(route_path('users'))
59
75
60 def test_show_users_data(self, xhr_header):
76 def test_show_users_data(self, xhr_header):
61 self.log_user()
77 self.log_user()
62 response = self.app.get(route_path(
78 response = self.app.get(route_path(
63 'users_data'), extra_environ=xhr_header)
79 'users_data'), extra_environ=xhr_header)
64
80
65 all_users = User.query().filter(
81 all_users = User.query().filter(
66 User.username != User.DEFAULT_USER).count()
82 User.username != User.DEFAULT_USER).count()
67 assert response.json['recordsTotal'] == all_users
83 assert response.json['recordsTotal'] == all_users
68
84
69 def test_show_users_data_filtered(self, xhr_header):
85 def test_show_users_data_filtered(self, xhr_header):
70 self.log_user()
86 self.log_user()
71 response = self.app.get(route_path(
87 response = self.app.get(route_path(
72 'users_data', params={'search[value]': 'empty_search'}),
88 'users_data', params={'search[value]': 'empty_search'}),
73 extra_environ=xhr_header)
89 extra_environ=xhr_header)
74
90
75 all_users = User.query().filter(
91 all_users = User.query().filter(
76 User.username != User.DEFAULT_USER).count()
92 User.username != User.DEFAULT_USER).count()
77 assert response.json['recordsTotal'] == all_users
93 assert response.json['recordsTotal'] == all_users
78 assert response.json['recordsFiltered'] == 0
94 assert response.json['recordsFiltered'] == 0
79
95
80 def test_auth_tokens_default_user(self):
96 def test_auth_tokens_default_user(self):
81 self.log_user()
97 self.log_user()
82 user = User.get_default_user()
98 user = User.get_default_user()
83 response = self.app.get(
99 response = self.app.get(
84 route_path('edit_user_auth_tokens', user_id=user.user_id),
100 route_path('edit_user_auth_tokens', user_id=user.user_id),
85 status=302)
101 status=302)
86
102
87 def test_auth_tokens(self):
103 def test_auth_tokens(self):
88 self.log_user()
104 self.log_user()
89
105
90 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
106 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
91 response = self.app.get(
107 response = self.app.get(
92 route_path('edit_user_auth_tokens', user_id=user.user_id))
108 route_path('edit_user_auth_tokens', user_id=user.user_id))
93 for token in user.auth_tokens:
109 for token in user.auth_tokens:
94 response.mustcontain(token)
110 response.mustcontain(token)
95 response.mustcontain('never')
111 response.mustcontain('never')
96
112
97 @pytest.mark.parametrize("desc, lifetime", [
113 @pytest.mark.parametrize("desc, lifetime", [
98 ('forever', -1),
114 ('forever', -1),
99 ('5mins', 60*5),
115 ('5mins', 60*5),
100 ('30days', 60*60*24*30),
116 ('30days', 60*60*24*30),
101 ])
117 ])
102 def test_add_auth_token(self, desc, lifetime, user_util):
118 def test_add_auth_token(self, desc, lifetime, user_util):
103 self.log_user()
119 self.log_user()
104 user = user_util.create_user()
120 user = user_util.create_user()
105 user_id = user.user_id
121 user_id = user.user_id
106
122
107 response = self.app.post(
123 response = self.app.post(
108 route_path('edit_user_auth_tokens_add', user_id=user_id),
124 route_path('edit_user_auth_tokens_add', user_id=user_id),
109 {'description': desc, 'lifetime': lifetime,
125 {'description': desc, 'lifetime': lifetime,
110 'csrf_token': self.csrf_token})
126 'csrf_token': self.csrf_token})
111 assert_session_flash(response, 'Auth token successfully created')
127 assert_session_flash(response, 'Auth token successfully created')
112
128
113 response = response.follow()
129 response = response.follow()
114 user = User.get(user_id)
130 user = User.get(user_id)
115 for auth_token in user.auth_tokens:
131 for auth_token in user.auth_tokens:
116 response.mustcontain(auth_token)
132 response.mustcontain(auth_token)
117
133
118 def test_delete_auth_token(self, user_util):
134 def test_delete_auth_token(self, user_util):
119 self.log_user()
135 self.log_user()
120 user = user_util.create_user()
136 user = user_util.create_user()
121 user_id = user.user_id
137 user_id = user.user_id
122 keys = user.extra_auth_tokens
138 keys = user.extra_auth_tokens
123 assert 2 == len(keys)
139 assert 2 == len(keys)
124
140
125 response = self.app.post(
141 response = self.app.post(
126 route_path('edit_user_auth_tokens_add', user_id=user_id),
142 route_path('edit_user_auth_tokens_add', user_id=user_id),
127 {'description': 'desc', 'lifetime': -1,
143 {'description': 'desc', 'lifetime': -1,
128 'csrf_token': self.csrf_token})
144 'csrf_token': self.csrf_token})
129 assert_session_flash(response, 'Auth token successfully created')
145 assert_session_flash(response, 'Auth token successfully created')
130 response.follow()
146 response.follow()
131
147
132 # now delete our key
148 # now delete our key
133 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
149 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
134 assert 3 == len(keys)
150 assert 3 == len(keys)
135
151
136 response = self.app.post(
152 response = self.app.post(
137 route_path('edit_user_auth_tokens_delete', user_id=user_id),
153 route_path('edit_user_auth_tokens_delete', user_id=user_id),
138 {'del_auth_token': keys[0].api_key, 'csrf_token': self.csrf_token})
154 {'del_auth_token': keys[0].user_api_key_id,
155 'csrf_token': self.csrf_token})
139
156
140 assert_session_flash(response, 'Auth token successfully deleted')
157 assert_session_flash(response, 'Auth token successfully deleted')
141 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
158 keys = UserApiKeys.query().filter(UserApiKeys.user_id == user_id).all()
142 assert 2 == len(keys)
159 assert 2 == len(keys)
160
161 def test_ips(self):
162 self.log_user()
163 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
164 response = self.app.get(route_path('edit_user_ips', user_id=user.user_id))
165 response.mustcontain('All IP addresses are allowed')
166
167 @pytest.mark.parametrize("test_name, ip, ip_range, failure", [
168 ('127/24', '127.0.0.1/24', '127.0.0.0 - 127.0.0.255', False),
169 ('10/32', '10.0.0.10/32', '10.0.0.10 - 10.0.0.10', False),
170 ('0/16', '0.0.0.0/16', '0.0.0.0 - 0.0.255.255', False),
171 ('0/8', '0.0.0.0/8', '0.0.0.0 - 0.255.255.255', False),
172 ('127_bad_mask', '127.0.0.1/99', '127.0.0.1 - 127.0.0.1', True),
173 ('127_bad_ip', 'foobar', 'foobar', True),
174 ])
175 def test_ips_add(self, user_util, test_name, ip, ip_range, failure):
176 self.log_user()
177 user = user_util.create_user(username=test_name)
178 user_id = user.user_id
179
180 response = self.app.post(
181 route_path('edit_user_ips_add', user_id=user_id),
182 params={'new_ip': ip, 'csrf_token': self.csrf_token})
183
184 if failure:
185 assert_session_flash(
186 response, 'Please enter a valid IPv4 or IpV6 address')
187 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
188
189 response.mustcontain(no=[ip])
190 response.mustcontain(no=[ip_range])
191
192 else:
193 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
194 response.mustcontain(ip)
195 response.mustcontain(ip_range)
196
197 def test_ips_delete(self, user_util):
198 self.log_user()
199 user = user_util.create_user()
200 user_id = user.user_id
201 ip = '127.0.0.1/32'
202 ip_range = '127.0.0.1 - 127.0.0.1'
203 new_ip = UserModel().add_extra_ip(user_id, ip)
204 Session().commit()
205 new_ip_id = new_ip.ip_id
206
207 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
208 response.mustcontain(ip)
209 response.mustcontain(ip_range)
210
211 self.app.post(
212 route_path('edit_user_ips_delete', user_id=user_id),
213 params={'del_ip_id': new_ip_id, 'csrf_token': self.csrf_token})
214
215 response = self.app.get(route_path('edit_user_ips', user_id=user_id))
216 response.mustcontain('All IP addresses are allowed')
217 response.mustcontain(no=[ip])
218 response.mustcontain(no=[ip_range])
219
220 def test_emails(self):
221 self.log_user()
222 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
223 response = self.app.get(route_path('edit_user_emails', user_id=user.user_id))
224 response.mustcontain('No additional emails specified')
225
226 def test_emails_add(self, user_util):
227 self.log_user()
228 user = user_util.create_user()
229 user_id = user.user_id
230
231 self.app.post(
232 route_path('edit_user_emails_add', user_id=user_id),
233 params={'new_email': 'example@rhodecode.com',
234 'csrf_token': self.csrf_token})
235
236 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
237 response.mustcontain('example@rhodecode.com')
238
239 def test_emails_add_existing_email(self, user_util, user_regular):
240 existing_email = user_regular.email
241
242 self.log_user()
243 user = user_util.create_user()
244 user_id = user.user_id
245
246 response = self.app.post(
247 route_path('edit_user_emails_add', user_id=user_id),
248 params={'new_email': existing_email,
249 'csrf_token': self.csrf_token})
250 assert_session_flash(
251 response, 'This e-mail address is already taken')
252
253 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
254 response.mustcontain(no=[existing_email])
255
256 def test_emails_delete(self, user_util):
257 self.log_user()
258 user = user_util.create_user()
259 user_id = user.user_id
260
261 self.app.post(
262 route_path('edit_user_emails_add', user_id=user_id),
263 params={'new_email': 'example@rhodecode.com',
264 'csrf_token': self.csrf_token})
265
266 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
267 response.mustcontain('example@rhodecode.com')
268
269 user_email = UserEmailMap.query()\
270 .filter(UserEmailMap.email == 'example@rhodecode.com') \
271 .filter(UserEmailMap.user_id == user_id)\
272 .one()
273
274 del_email_id = user_email.email_id
275 self.app.post(
276 route_path('edit_user_emails_delete', user_id=user_id),
277 params={'del_email_id': del_email_id,
278 'csrf_token': self.csrf_token})
279
280 response = self.app.get(route_path('edit_user_emails', user_id=user_id))
281 response.mustcontain(no=['example@rhodecode.com']) No newline at end of file
@@ -1,308 +1,515 b''
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 logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23
24
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26 from pyramid.view import view_config
26 from sqlalchemy.sql.functions import coalesce
27 from sqlalchemy.sql.functions import coalesce
27
28
28 from rhodecode.lib.helpers import Page
29 from rhodecode.apps._base import BaseAppView, DataGridAppView
30
31 from rhodecode.lib import audit_logger
29 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 from rhodecode.model.auth_token import AuthTokenModel
38 from rhodecode.model.auth_token import AuthTokenModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
39 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.db import User, or_
41 from rhodecode.model.db import User, or_, UserIpMap, UserEmailMap, UserApiKeys
41 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
42
43
43 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
44
45
45
46
46 class AdminUsersView(BaseAppView, DataGridAppView):
47 class AdminUsersView(BaseAppView, DataGridAppView):
47 ALLOW_SCOPED_TOKENS = False
48 ALLOW_SCOPED_TOKENS = False
48 """
49 """
49 This view has alternative version inside EE, if modified please take a look
50 This view has alternative version inside EE, if modified please take a look
50 in there as well.
51 in there as well.
51 """
52 """
52
53
53 def load_default_context(self):
54 def load_default_context(self):
54 c = self._get_local_tmpl_context()
55 c = self._get_local_tmpl_context()
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 self._register_global_c(c)
57 self._register_global_c(c)
57 return c
58 return c
58
59
59 def _redirect_for_default_user(self, username):
60 def _redirect_for_default_user(self, username):
60 _ = self.request.translate
61 _ = self.request.translate
61 if username == User.DEFAULT_USER:
62 if username == User.DEFAULT_USER:
62 h.flash(_("You can't edit this user"), category='warning')
63 h.flash(_("You can't edit this user"), category='warning')
63 # TODO(marcink): redirect to 'users' admin panel once this
64 # TODO(marcink): redirect to 'users' admin panel once this
64 # is a pyramid view
65 # is a pyramid view
65 raise HTTPFound('/')
66 raise HTTPFound('/')
66
67
67 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
68 @view_config(
69 @view_config(
69 route_name='users', request_method='GET',
70 route_name='users', request_method='GET',
70 renderer='rhodecode:templates/admin/users/users.mako')
71 renderer='rhodecode:templates/admin/users/users.mako')
71 def users_list(self):
72 def users_list(self):
72 c = self.load_default_context()
73 c = self.load_default_context()
73 return self._get_template_context(c)
74 return self._get_template_context(c)
74
75
75 @HasPermissionAllDecorator('hg.admin')
76 @HasPermissionAllDecorator('hg.admin')
76 @view_config(
77 @view_config(
77 # renderer defined below
78 # renderer defined below
78 route_name='users_data', request_method='GET',
79 route_name='users_data', request_method='GET',
79 renderer='json_ext', xhr=True)
80 renderer='json_ext', xhr=True)
80 def users_list_data(self):
81 def users_list_data(self):
81 draw, start, limit = self._extract_chunk(self.request)
82 draw, start, limit = self._extract_chunk(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
83 search_q, order_by, order_dir = self._extract_ordering(self.request)
83
84
84 _render = PartialRenderer('data_table/_dt_elements.mako')
85 _render = PartialRenderer('data_table/_dt_elements.mako')
85
86
86 def user_actions(user_id, username):
87 def user_actions(user_id, username):
87 return _render("user_actions", user_id, username)
88 return _render("user_actions", user_id, username)
88
89
89 users_data_total_count = User.query()\
90 users_data_total_count = User.query()\
90 .filter(User.username != User.DEFAULT_USER) \
91 .filter(User.username != User.DEFAULT_USER) \
91 .count()
92 .count()
92
93
93 # json generate
94 # json generate
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
95 base_q = User.query().filter(User.username != User.DEFAULT_USER)
95
96
96 if search_q:
97 if search_q:
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
98 like_expression = u'%{}%'.format(safe_unicode(search_q))
98 base_q = base_q.filter(or_(
99 base_q = base_q.filter(or_(
99 User.username.ilike(like_expression),
100 User.username.ilike(like_expression),
100 User._email.ilike(like_expression),
101 User._email.ilike(like_expression),
101 User.name.ilike(like_expression),
102 User.name.ilike(like_expression),
102 User.lastname.ilike(like_expression),
103 User.lastname.ilike(like_expression),
103 ))
104 ))
104
105
105 users_data_total_filtered_count = base_q.count()
106 users_data_total_filtered_count = base_q.count()
106
107
107 sort_col = getattr(User, order_by, None)
108 sort_col = getattr(User, order_by, None)
108 if sort_col:
109 if sort_col:
109 if order_dir == 'asc':
110 if order_dir == 'asc':
110 # handle null values properly to order by NULL last
111 # handle null values properly to order by NULL last
111 if order_by in ['last_activity']:
112 if order_by in ['last_activity']:
112 sort_col = coalesce(sort_col, datetime.date.max)
113 sort_col = coalesce(sort_col, datetime.date.max)
113 sort_col = sort_col.asc()
114 sort_col = sort_col.asc()
114 else:
115 else:
115 # handle null values properly to order by NULL last
116 # handle null values properly to order by NULL last
116 if order_by in ['last_activity']:
117 if order_by in ['last_activity']:
117 sort_col = coalesce(sort_col, datetime.date.min)
118 sort_col = coalesce(sort_col, datetime.date.min)
118 sort_col = sort_col.desc()
119 sort_col = sort_col.desc()
119
120
120 base_q = base_q.order_by(sort_col)
121 base_q = base_q.order_by(sort_col)
121 base_q = base_q.offset(start).limit(limit)
122 base_q = base_q.offset(start).limit(limit)
122
123
123 users_list = base_q.all()
124 users_list = base_q.all()
124
125
125 users_data = []
126 users_data = []
126 for user in users_list:
127 for user in users_list:
127 users_data.append({
128 users_data.append({
128 "username": h.gravatar_with_user(user.username),
129 "username": h.gravatar_with_user(user.username),
129 "email": user.email,
130 "email": user.email,
130 "first_name": user.first_name,
131 "first_name": user.first_name,
131 "last_name": user.last_name,
132 "last_name": user.last_name,
132 "last_login": h.format_date(user.last_login),
133 "last_login": h.format_date(user.last_login),
133 "last_activity": h.format_date(user.last_activity),
134 "last_activity": h.format_date(user.last_activity),
134 "active": h.bool2icon(user.active),
135 "active": h.bool2icon(user.active),
135 "active_raw": user.active,
136 "active_raw": user.active,
136 "admin": h.bool2icon(user.admin),
137 "admin": h.bool2icon(user.admin),
137 "extern_type": user.extern_type,
138 "extern_type": user.extern_type,
138 "extern_name": user.extern_name,
139 "extern_name": user.extern_name,
139 "action": user_actions(user.user_id, user.username),
140 "action": user_actions(user.user_id, user.username),
140 })
141 })
141
142
142 data = ({
143 data = ({
143 'draw': draw,
144 'draw': draw,
144 'data': users_data,
145 'data': users_data,
145 'recordsTotal': users_data_total_count,
146 'recordsTotal': users_data_total_count,
146 'recordsFiltered': users_data_total_filtered_count,
147 'recordsFiltered': users_data_total_filtered_count,
147 })
148 })
148
149
149 return data
150 return data
150
151
151 @LoginRequired()
152 @LoginRequired()
152 @HasPermissionAllDecorator('hg.admin')
153 @HasPermissionAllDecorator('hg.admin')
153 @view_config(
154 @view_config(
154 route_name='edit_user_auth_tokens', request_method='GET',
155 route_name='edit_user_auth_tokens', request_method='GET',
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
156 renderer='rhodecode:templates/admin/users/user_edit.mako')
156 def auth_tokens(self):
157 def auth_tokens(self):
157 _ = self.request.translate
158 _ = self.request.translate
158 c = self.load_default_context()
159 c = self.load_default_context()
159
160
160 user_id = self.request.matchdict.get('user_id')
161 user_id = self.request.matchdict.get('user_id')
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
162 c.user = User.get_or_404(user_id, pyramid_exc=True)
162 self._redirect_for_default_user(c.user.username)
163 self._redirect_for_default_user(c.user.username)
163
164
164 c.active = 'auth_tokens'
165 c.active = 'auth_tokens'
165
166
166 c.lifetime_values = [
167 c.lifetime_values = [
167 (str(-1), _('forever')),
168 (str(-1), _('forever')),
168 (str(5), _('5 minutes')),
169 (str(5), _('5 minutes')),
169 (str(60), _('1 hour')),
170 (str(60), _('1 hour')),
170 (str(60 * 24), _('1 day')),
171 (str(60 * 24), _('1 day')),
171 (str(60 * 24 * 30), _('1 month')),
172 (str(60 * 24 * 30), _('1 month')),
172 ]
173 ]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
174 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
174 c.role_values = [
175 c.role_values = [
175 (x, AuthTokenModel.cls._get_role_name(x))
176 (x, AuthTokenModel.cls._get_role_name(x))
176 for x in AuthTokenModel.cls.ROLES]
177 for x in AuthTokenModel.cls.ROLES]
177 c.role_options = [(c.role_values, _("Role"))]
178 c.role_options = [(c.role_values, _("Role"))]
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
179 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
179 c.user.user_id, show_expired=True)
180 c.user.user_id, show_expired=True)
180 return self._get_template_context(c)
181 return self._get_template_context(c)
181
182
182 def maybe_attach_token_scope(self, token):
183 def maybe_attach_token_scope(self, token):
183 # implemented in EE edition
184 # implemented in EE edition
184 pass
185 pass
185
186
186 @LoginRequired()
187 @LoginRequired()
187 @HasPermissionAllDecorator('hg.admin')
188 @HasPermissionAllDecorator('hg.admin')
188 @CSRFRequired()
189 @CSRFRequired()
189 @view_config(
190 @view_config(
190 route_name='edit_user_auth_tokens_add', request_method='POST')
191 route_name='edit_user_auth_tokens_add', request_method='POST')
191 def auth_tokens_add(self):
192 def auth_tokens_add(self):
192 _ = self.request.translate
193 _ = self.request.translate
193 c = self.load_default_context()
194 c = self.load_default_context()
194
195
195 user_id = self.request.matchdict.get('user_id')
196 user_id = self.request.matchdict.get('user_id')
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
197 c.user = User.get_or_404(user_id, pyramid_exc=True)
198
197 self._redirect_for_default_user(c.user.username)
199 self._redirect_for_default_user(c.user.username)
198
200
201 user_data = c.user.get_api_data()
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
202 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
200 description = self.request.POST.get('description')
203 description = self.request.POST.get('description')
201 role = self.request.POST.get('role')
204 role = self.request.POST.get('role')
202
205
203 token = AuthTokenModel().create(
206 token = AuthTokenModel().create(
204 c.user.user_id, description, lifetime, role)
207 c.user.user_id, description, lifetime, role)
208 token_data = token.get_api_data()
209
205 self.maybe_attach_token_scope(token)
210 self.maybe_attach_token_scope(token)
211 audit_logger.store(
212 action='user.edit.token.add',
213 action_data={'data': {'token': token_data, 'user': user_data}},
214 user=self._rhodecode_user, )
206 Session().commit()
215 Session().commit()
207
216
208 h.flash(_("Auth token successfully created"), category='success')
217 h.flash(_("Auth token successfully created"), category='success')
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
218 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
210
219
211 @LoginRequired()
220 @LoginRequired()
212 @HasPermissionAllDecorator('hg.admin')
221 @HasPermissionAllDecorator('hg.admin')
213 @CSRFRequired()
222 @CSRFRequired()
214 @view_config(
223 @view_config(
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
224 route_name='edit_user_auth_tokens_delete', request_method='POST')
216 def auth_tokens_delete(self):
225 def auth_tokens_delete(self):
217 _ = self.request.translate
226 _ = self.request.translate
218 c = self.load_default_context()
227 c = self.load_default_context()
219
228
220 user_id = self.request.matchdict.get('user_id')
229 user_id = self.request.matchdict.get('user_id')
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
230 c.user = User.get_or_404(user_id, pyramid_exc=True)
222 self._redirect_for_default_user(c.user.username)
231 self._redirect_for_default_user(c.user.username)
232 user_data = c.user.get_api_data()
223
233
224 del_auth_token = self.request.POST.get('del_auth_token')
234 del_auth_token = self.request.POST.get('del_auth_token')
225
235
226 if del_auth_token:
236 if del_auth_token:
237 token = UserApiKeys.get_or_404(del_auth_token, pyramid_exc=True)
238 token_data = token.get_api_data()
239
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
240 AuthTokenModel().delete(del_auth_token, c.user.user_id)
241 audit_logger.store(
242 action='user.edit.token.delete',
243 action_data={'data': {'token': token_data, 'user': user_data}},
244 user=self._rhodecode_user,)
228 Session().commit()
245 Session().commit()
229 h.flash(_("Auth token successfully deleted"), category='success')
246 h.flash(_("Auth token successfully deleted"), category='success')
230
247
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
248 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
232
249
250
251
252
253
254
255 @LoginRequired()
256 @HasPermissionAllDecorator('hg.admin')
257 @view_config(
258 route_name='edit_user_emails', request_method='GET',
259 renderer='rhodecode:templates/admin/users/user_edit.mako')
260 def emails(self):
261 _ = self.request.translate
262 c = self.load_default_context()
263
264 user_id = self.request.matchdict.get('user_id')
265 c.user = User.get_or_404(user_id, pyramid_exc=True)
266 self._redirect_for_default_user(c.user.username)
267
268 c.active = 'emails'
269 c.user_email_map = UserEmailMap.query() \
270 .filter(UserEmailMap.user == c.user).all()
271
272 return self._get_template_context(c)
273
274 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
276 @CSRFRequired()
277 @view_config(
278 route_name='edit_user_emails_add', request_method='POST')
279 def emails_add(self):
280 _ = self.request.translate
281 c = self.load_default_context()
282
283 user_id = self.request.matchdict.get('user_id')
284 c.user = User.get_or_404(user_id, pyramid_exc=True)
285 self._redirect_for_default_user(c.user.username)
286
287 email = self.request.POST.get('new_email')
288 user_data = c.user.get_api_data()
289 try:
290 UserModel().add_extra_email(c.user.user_id, email)
291 audit_logger.store_web(
292 'user.edit.email.add',
293 action_data={'email': email, 'user': user_data},
294 user=self._rhodecode_user)
295 Session().commit()
296 h.flash(_("Added new email address `%s` for user account") % email,
297 category='success')
298 except formencode.Invalid as error:
299 msg = error.error_dict['email']
300 h.flash(msg, category='error')
301 except Exception:
302 log.exception("Exception during email saving")
303 h.flash(_('An error occurred during email saving'),
304 category='error')
305 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
306
307 @LoginRequired()
308 @HasPermissionAllDecorator('hg.admin')
309 @CSRFRequired()
310 @view_config(
311 route_name='edit_user_emails_delete', request_method='POST')
312 def emails_delete(self):
313 _ = self.request.translate
314 c = self.load_default_context()
315
316 user_id = self.request.matchdict.get('user_id')
317 c.user = User.get_or_404(user_id, pyramid_exc=True)
318 self._redirect_for_default_user(c.user.username)
319
320 email_id = self.request.POST.get('del_email_id')
321 user_model = UserModel()
322
323 email = UserEmailMap.query().get(email_id).email
324 user_data = c.user.get_api_data()
325 user_model.delete_extra_email(c.user.user_id, email_id)
326 audit_logger.store_web(
327 'user.edit.email.delete',
328 action_data={'email': email, 'user': user_data},
329 user=self._rhodecode_user)
330 Session().commit()
331 h.flash(_("Removed email address from user account"),
332 category='success')
333 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
334
335 @LoginRequired()
336 @HasPermissionAllDecorator('hg.admin')
337 @view_config(
338 route_name='edit_user_ips', request_method='GET',
339 renderer='rhodecode:templates/admin/users/user_edit.mako')
340 def ips(self):
341 _ = self.request.translate
342 c = self.load_default_context()
343
344 user_id = self.request.matchdict.get('user_id')
345 c.user = User.get_or_404(user_id, pyramid_exc=True)
346 self._redirect_for_default_user(c.user.username)
347
348 c.active = 'ips'
349 c.user_ip_map = UserIpMap.query() \
350 .filter(UserIpMap.user == c.user).all()
351
352 c.inherit_default_ips = c.user.inherit_default_permissions
353 c.default_user_ip_map = UserIpMap.query() \
354 .filter(UserIpMap.user == User.get_default_user()).all()
355
356 return self._get_template_context(c)
357
358 @LoginRequired()
359 @HasPermissionAllDecorator('hg.admin')
360 @CSRFRequired()
361 @view_config(
362 route_name='edit_user_ips_add', request_method='POST')
363 def ips_add(self):
364 _ = self.request.translate
365 c = self.load_default_context()
366
367 user_id = self.request.matchdict.get('user_id')
368 c.user = User.get_or_404(user_id, pyramid_exc=True)
369 # NOTE(marcink): this view is allowed for default users, as we can
370 # edit their IP white list
371
372 user_model = UserModel()
373 desc = self.request.POST.get('description')
374 try:
375 ip_list = user_model.parse_ip_range(
376 self.request.POST.get('new_ip'))
377 except Exception as e:
378 ip_list = []
379 log.exception("Exception during ip saving")
380 h.flash(_('An error occurred during ip saving:%s' % (e,)),
381 category='error')
382 added = []
383 user_data = c.user.get_api_data()
384 for ip in ip_list:
385 try:
386 user_model.add_extra_ip(c.user.user_id, ip, desc)
387 audit_logger.store_web(
388 'user.edit.ip.add',
389 action_data={'ip': ip, 'user': user_data},
390 user=self._rhodecode_user)
391 Session().commit()
392 added.append(ip)
393 except formencode.Invalid as error:
394 msg = error.error_dict['ip']
395 h.flash(msg, category='error')
396 except Exception:
397 log.exception("Exception during ip saving")
398 h.flash(_('An error occurred during ip saving'),
399 category='error')
400 if added:
401 h.flash(
402 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
403 category='success')
404 if 'default_user' in self.request.POST:
405 # case for editing global IP list we do it for 'DEFAULT' user
406 raise HTTPFound(h.route_path('admin_permissions_ips'))
407 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
408
409 @LoginRequired()
410 @HasPermissionAllDecorator('hg.admin')
411 @CSRFRequired()
412 @view_config(
413 route_name='edit_user_ips_delete', request_method='POST')
414 def ips_delete(self):
415 _ = self.request.translate
416 c = self.load_default_context()
417
418 user_id = self.request.matchdict.get('user_id')
419 c.user = User.get_or_404(user_id, pyramid_exc=True)
420 # NOTE(marcink): this view is allowed for default users, as we can
421 # edit their IP white list
422
423 ip_id = self.request.POST.get('del_ip_id')
424 user_model = UserModel()
425 user_data = c.user.get_api_data()
426 ip = UserIpMap.query().get(ip_id).ip_addr
427 user_model.delete_extra_ip(c.user.user_id, ip_id)
428 audit_logger.store_web(
429 'user.edit.ip.delete',
430 action_data={'ip': ip, 'user': user_data},
431 user=self._rhodecode_user)
432 Session().commit()
433 h.flash(_("Removed ip address from user whitelist"), category='success')
434
435 if 'default_user' in self.request.POST:
436 # case for editing global IP list we do it for 'DEFAULT' user
437 raise HTTPFound(h.route_path('admin_permissions_ips'))
438 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
439
233 @LoginRequired()
440 @LoginRequired()
234 @HasPermissionAllDecorator('hg.admin')
441 @HasPermissionAllDecorator('hg.admin')
235 @view_config(
442 @view_config(
236 route_name='edit_user_groups_management', request_method='GET',
443 route_name='edit_user_groups_management', request_method='GET',
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
444 renderer='rhodecode:templates/admin/users/user_edit.mako')
238 def groups_management(self):
445 def groups_management(self):
239 c = self.load_default_context()
446 c = self.load_default_context()
240
447
241 user_id = self.request.matchdict.get('user_id')
448 user_id = self.request.matchdict.get('user_id')
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
449 c.user = User.get_or_404(user_id, pyramid_exc=True)
243 c.data = c.user.group_member
450 c.data = c.user.group_member
244 self._redirect_for_default_user(c.user.username)
451 self._redirect_for_default_user(c.user.username)
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
452 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
246 for group in c.user.group_member]
453 for group in c.user.group_member]
247 c.groups = json.dumps(groups)
454 c.groups = json.dumps(groups)
248 c.active = 'groups'
455 c.active = 'groups'
249
456
250 return self._get_template_context(c)
457 return self._get_template_context(c)
251
458
252 @LoginRequired()
459 @LoginRequired()
253 @HasPermissionAllDecorator('hg.admin')
460 @HasPermissionAllDecorator('hg.admin')
254 @CSRFRequired()
461 @CSRFRequired()
255 @view_config(
462 @view_config(
256 route_name='edit_user_groups_management_updates', request_method='POST')
463 route_name='edit_user_groups_management_updates', request_method='POST')
257 def groups_management_updates(self):
464 def groups_management_updates(self):
258 _ = self.request.translate
465 _ = self.request.translate
259 c = self.load_default_context()
466 c = self.load_default_context()
260
467
261 user_id = self.request.matchdict.get('user_id')
468 user_id = self.request.matchdict.get('user_id')
262 c.user = User.get_or_404(user_id, pyramid_exc=True)
469 c.user = User.get_or_404(user_id, pyramid_exc=True)
263 self._redirect_for_default_user(c.user.username)
470 self._redirect_for_default_user(c.user.username)
264
471
265 users_groups = set(self.request.POST.getall('users_group_id'))
472 users_groups = set(self.request.POST.getall('users_group_id'))
266 users_groups_model = []
473 users_groups_model = []
267
474
268 for ugid in users_groups:
475 for ugid in users_groups:
269 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
476 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
270 user_group_model = UserGroupModel()
477 user_group_model = UserGroupModel()
271 user_group_model.change_groups(c.user, users_groups_model)
478 user_group_model.change_groups(c.user, users_groups_model)
272
479
273 Session().commit()
480 Session().commit()
274 c.active = 'user_groups_management'
481 c.active = 'user_groups_management'
275 h.flash(_("Groups successfully changed"), category='success')
482 h.flash(_("Groups successfully changed"), category='success')
276
483
277 return HTTPFound(h.route_path(
484 return HTTPFound(h.route_path(
278 'edit_user_groups_management', user_id=user_id))
485 'edit_user_groups_management', user_id=user_id))
279
486
280 @LoginRequired()
487 @LoginRequired()
281 @HasPermissionAllDecorator('hg.admin')
488 @HasPermissionAllDecorator('hg.admin')
282 @view_config(
489 @view_config(
283 route_name='edit_user_audit_logs', request_method='GET',
490 route_name='edit_user_audit_logs', request_method='GET',
284 renderer='rhodecode:templates/admin/users/user_edit.mako')
491 renderer='rhodecode:templates/admin/users/user_edit.mako')
285 def user_audit_logs(self):
492 def user_audit_logs(self):
286 _ = self.request.translate
493 _ = self.request.translate
287 c = self.load_default_context()
494 c = self.load_default_context()
288
495
289 user_id = self.request.matchdict.get('user_id')
496 user_id = self.request.matchdict.get('user_id')
290 c.user = User.get_or_404(user_id, pyramid_exc=True)
497 c.user = User.get_or_404(user_id, pyramid_exc=True)
291 self._redirect_for_default_user(c.user.username)
498 self._redirect_for_default_user(c.user.username)
292 c.active = 'audit'
499 c.active = 'audit'
293
500
294 p = safe_int(self.request.GET.get('page', 1), 1)
501 p = safe_int(self.request.GET.get('page', 1), 1)
295
502
296 filter_term = self.request.GET.get('filter')
503 filter_term = self.request.GET.get('filter')
297 user_log = UserModel().get_user_log(c.user, filter_term)
504 user_log = UserModel().get_user_log(c.user, filter_term)
298
505
299 def url_generator(**kw):
506 def url_generator(**kw):
300 if filter_term:
507 if filter_term:
301 kw['filter'] = filter_term
508 kw['filter'] = filter_term
302 return self.request.current_route_path(_query=kw)
509 return self.request.current_route_path(_query=kw)
303
510
304 c.audit_logs = Page(user_log, page=p, items_per_page=10,
511 c.audit_logs = h.Page(
305 url=url_generator)
512 user_log, page=p, items_per_page=10, url=url_generator)
306 c.filter_term = filter_term
513 c.filter_term = filter_term
307 return self._get_template_context(c)
514 return self._get_template_context(c)
308
515
@@ -1,958 +1,945 b''
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 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(
120 rmap = JSRoutesMapper(
121 directory=config['pylons.paths']['controllers'],
121 directory=config['pylons.paths']['controllers'],
122 always_scan=config['debug'])
122 always_scan=config['debug'])
123 rmap.minimization = False
123 rmap.minimization = False
124 rmap.explicit = False
124 rmap.explicit = False
125
125
126 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.model import repo, repo_group
127 from rhodecode.model import repo, repo_group
128
128
129 def check_repo(environ, match_dict):
129 def check_repo(environ, match_dict):
130 """
130 """
131 check for valid repository for proper 404 handling
131 check for valid repository for proper 404 handling
132
132
133 :param environ:
133 :param environ:
134 :param match_dict:
134 :param match_dict:
135 """
135 """
136 repo_name = match_dict.get('repo_name')
136 repo_name = match_dict.get('repo_name')
137
137
138 if match_dict.get('f_path'):
138 if match_dict.get('f_path'):
139 # fix for multiple initial slashes that causes errors
139 # fix for multiple initial slashes that causes errors
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 repo_model = repo.RepoModel()
141 repo_model = repo.RepoModel()
142 by_name_match = repo_model.get_by_repo_name(repo_name)
142 by_name_match = repo_model.get_by_repo_name(repo_name)
143 # if we match quickly from database, short circuit the operation,
143 # if we match quickly from database, short circuit the operation,
144 # and validate repo based on the type.
144 # and validate repo based on the type.
145 if by_name_match:
145 if by_name_match:
146 return True
146 return True
147
147
148 by_id_match = repo_model.get_repo_by_id(repo_name)
148 by_id_match = repo_model.get_repo_by_id(repo_name)
149 if by_id_match:
149 if by_id_match:
150 repo_name = by_id_match.repo_name
150 repo_name = by_id_match.repo_name
151 match_dict['repo_name'] = repo_name
151 match_dict['repo_name'] = repo_name
152 return True
152 return True
153
153
154 return False
154 return False
155
155
156 def check_group(environ, match_dict):
156 def check_group(environ, match_dict):
157 """
157 """
158 check for valid repository group path for proper 404 handling
158 check for valid repository group path for proper 404 handling
159
159
160 :param environ:
160 :param environ:
161 :param match_dict:
161 :param match_dict:
162 """
162 """
163 repo_group_name = match_dict.get('group_name')
163 repo_group_name = match_dict.get('group_name')
164 repo_group_model = repo_group.RepoGroupModel()
164 repo_group_model = repo_group.RepoGroupModel()
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 if by_name_match:
166 if by_name_match:
167 return True
167 return True
168
168
169 return False
169 return False
170
170
171 def check_user_group(environ, match_dict):
171 def check_user_group(environ, match_dict):
172 """
172 """
173 check for valid user group for proper 404 handling
173 check for valid user group for proper 404 handling
174
174
175 :param environ:
175 :param environ:
176 :param match_dict:
176 :param match_dict:
177 """
177 """
178 return True
178 return True
179
179
180 def check_int(environ, match_dict):
180 def check_int(environ, match_dict):
181 return match_dict.get('id').isdigit()
181 return match_dict.get('id').isdigit()
182
182
183
183
184 #==========================================================================
184 #==========================================================================
185 # CUSTOM ROUTES HERE
185 # CUSTOM ROUTES HERE
186 #==========================================================================
186 #==========================================================================
187
187
188 # ping and pylons error test
188 # ping and pylons error test
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
189 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
190 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
191
191
192 # ADMIN REPOSITORY ROUTES
192 # ADMIN REPOSITORY ROUTES
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
193 with rmap.submapper(path_prefix=ADMIN_PREFIX,
194 controller='admin/repos') as m:
194 controller='admin/repos') as m:
195 m.connect('repos', '/repos',
195 m.connect('repos', '/repos',
196 action='create', conditions={'method': ['POST']})
196 action='create', conditions={'method': ['POST']})
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='index', conditions={'method': ['GET']})
198 action='index', conditions={'method': ['GET']})
199 m.connect('new_repo', '/create_repository', jsroute=True,
199 m.connect('new_repo', '/create_repository', jsroute=True,
200 action='create_repository', conditions={'method': ['GET']})
200 action='create_repository', conditions={'method': ['GET']})
201 m.connect('delete_repo', '/repos/{repo_name}',
201 m.connect('delete_repo', '/repos/{repo_name}',
202 action='delete', conditions={'method': ['DELETE']},
202 action='delete', conditions={'method': ['DELETE']},
203 requirements=URL_NAME_REQUIREMENTS)
203 requirements=URL_NAME_REQUIREMENTS)
204 m.connect('repo', '/repos/{repo_name}',
204 m.connect('repo', '/repos/{repo_name}',
205 action='show', conditions={'method': ['GET'],
205 action='show', conditions={'method': ['GET'],
206 'function': check_repo},
206 'function': check_repo},
207 requirements=URL_NAME_REQUIREMENTS)
207 requirements=URL_NAME_REQUIREMENTS)
208
208
209 # ADMIN REPOSITORY GROUPS ROUTES
209 # ADMIN REPOSITORY GROUPS ROUTES
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
210 with rmap.submapper(path_prefix=ADMIN_PREFIX,
211 controller='admin/repo_groups') as m:
211 controller='admin/repo_groups') as m:
212 m.connect('repo_groups', '/repo_groups',
212 m.connect('repo_groups', '/repo_groups',
213 action='create', conditions={'method': ['POST']})
213 action='create', conditions={'method': ['POST']})
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='index', conditions={'method': ['GET']})
215 action='index', conditions={'method': ['GET']})
216 m.connect('new_repo_group', '/repo_groups/new',
216 m.connect('new_repo_group', '/repo_groups/new',
217 action='new', conditions={'method': ['GET']})
217 action='new', conditions={'method': ['GET']})
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
218 m.connect('update_repo_group', '/repo_groups/{group_name}',
219 action='update', conditions={'method': ['PUT'],
219 action='update', conditions={'method': ['PUT'],
220 'function': check_group},
220 'function': check_group},
221 requirements=URL_NAME_REQUIREMENTS)
221 requirements=URL_NAME_REQUIREMENTS)
222
222
223 # EXTRAS REPO GROUP ROUTES
223 # EXTRAS REPO GROUP ROUTES
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
224 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
225 action='edit',
225 action='edit',
226 conditions={'method': ['GET'], 'function': check_group},
226 conditions={'method': ['GET'], 'function': check_group},
227 requirements=URL_NAME_REQUIREMENTS)
227 requirements=URL_NAME_REQUIREMENTS)
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
228 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
229 action='edit',
229 action='edit',
230 conditions={'method': ['PUT'], 'function': check_group},
230 conditions={'method': ['PUT'], 'function': check_group},
231 requirements=URL_NAME_REQUIREMENTS)
231 requirements=URL_NAME_REQUIREMENTS)
232
232
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
233 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
234 action='edit_repo_group_advanced',
234 action='edit_repo_group_advanced',
235 conditions={'method': ['GET'], 'function': check_group},
235 conditions={'method': ['GET'], 'function': check_group},
236 requirements=URL_NAME_REQUIREMENTS)
236 requirements=URL_NAME_REQUIREMENTS)
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
237 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
238 action='edit_repo_group_advanced',
238 action='edit_repo_group_advanced',
239 conditions={'method': ['PUT'], 'function': check_group},
239 conditions={'method': ['PUT'], 'function': check_group},
240 requirements=URL_NAME_REQUIREMENTS)
240 requirements=URL_NAME_REQUIREMENTS)
241
241
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
242 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
243 action='edit_repo_group_perms',
243 action='edit_repo_group_perms',
244 conditions={'method': ['GET'], 'function': check_group},
244 conditions={'method': ['GET'], 'function': check_group},
245 requirements=URL_NAME_REQUIREMENTS)
245 requirements=URL_NAME_REQUIREMENTS)
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
246 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
247 action='update_perms',
247 action='update_perms',
248 conditions={'method': ['PUT'], 'function': check_group},
248 conditions={'method': ['PUT'], 'function': check_group},
249 requirements=URL_NAME_REQUIREMENTS)
249 requirements=URL_NAME_REQUIREMENTS)
250
250
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
251 m.connect('delete_repo_group', '/repo_groups/{group_name}',
252 action='delete', conditions={'method': ['DELETE'],
252 action='delete', conditions={'method': ['DELETE'],
253 'function': check_group},
253 'function': check_group},
254 requirements=URL_NAME_REQUIREMENTS)
254 requirements=URL_NAME_REQUIREMENTS)
255
255
256 # ADMIN USER ROUTES
256 # ADMIN USER ROUTES
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
257 with rmap.submapper(path_prefix=ADMIN_PREFIX,
258 controller='admin/users') as m:
258 controller='admin/users') as m:
259 m.connect('users', '/users',
259 m.connect('users', '/users',
260 action='create', conditions={'method': ['POST']})
260 action='create', conditions={'method': ['POST']})
261 m.connect('new_user', '/users/new',
261 m.connect('new_user', '/users/new',
262 action='new', conditions={'method': ['GET']})
262 action='new', conditions={'method': ['GET']})
263 m.connect('update_user', '/users/{user_id}',
263 m.connect('update_user', '/users/{user_id}',
264 action='update', conditions={'method': ['PUT']})
264 action='update', conditions={'method': ['PUT']})
265 m.connect('delete_user', '/users/{user_id}',
265 m.connect('delete_user', '/users/{user_id}',
266 action='delete', conditions={'method': ['DELETE']})
266 action='delete', conditions={'method': ['DELETE']})
267 m.connect('edit_user', '/users/{user_id}/edit',
267 m.connect('edit_user', '/users/{user_id}/edit',
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
268 action='edit', conditions={'method': ['GET']}, jsroute=True)
269 m.connect('user', '/users/{user_id}',
269 m.connect('user', '/users/{user_id}',
270 action='show', conditions={'method': ['GET']})
270 action='show', conditions={'method': ['GET']})
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
271 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
272 action='reset_password', conditions={'method': ['POST']})
272 action='reset_password', conditions={'method': ['POST']})
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
273 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
274 action='create_personal_repo_group', conditions={'method': ['POST']})
274 action='create_personal_repo_group', conditions={'method': ['POST']})
275
275
276 # EXTRAS USER ROUTES
276 # EXTRAS USER ROUTES
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
277 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
278 action='edit_advanced', conditions={'method': ['GET']})
278 action='edit_advanced', conditions={'method': ['GET']})
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='update_advanced', conditions={'method': ['PUT']})
280 action='update_advanced', conditions={'method': ['PUT']})
281
281
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
282 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
283 action='edit_global_perms', conditions={'method': ['GET']})
283 action='edit_global_perms', conditions={'method': ['GET']})
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='update_global_perms', conditions={'method': ['PUT']})
285 action='update_global_perms', conditions={'method': ['PUT']})
286
286
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
287 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
288 action='edit_perms_summary', conditions={'method': ['GET']})
288 action='edit_perms_summary', conditions={'method': ['GET']})
289
289
290 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
291 action='edit_emails', conditions={'method': ['GET']})
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='add_email', conditions={'method': ['PUT']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='delete_email', conditions={'method': ['DELETE']})
296
297 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
298 action='edit_ips', conditions={'method': ['GET']})
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='add_ip', conditions={'method': ['PUT']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='delete_ip', conditions={'method': ['DELETE']})
303
290
304 # ADMIN USER GROUPS REST ROUTES
291 # ADMIN USER GROUPS REST ROUTES
305 with rmap.submapper(path_prefix=ADMIN_PREFIX,
292 with rmap.submapper(path_prefix=ADMIN_PREFIX,
306 controller='admin/user_groups') as m:
293 controller='admin/user_groups') as m:
307 m.connect('users_groups', '/user_groups',
294 m.connect('users_groups', '/user_groups',
308 action='create', conditions={'method': ['POST']})
295 action='create', conditions={'method': ['POST']})
309 m.connect('users_groups', '/user_groups',
296 m.connect('users_groups', '/user_groups',
310 action='index', conditions={'method': ['GET']})
297 action='index', conditions={'method': ['GET']})
311 m.connect('new_users_group', '/user_groups/new',
298 m.connect('new_users_group', '/user_groups/new',
312 action='new', conditions={'method': ['GET']})
299 action='new', conditions={'method': ['GET']})
313 m.connect('update_users_group', '/user_groups/{user_group_id}',
300 m.connect('update_users_group', '/user_groups/{user_group_id}',
314 action='update', conditions={'method': ['PUT']})
301 action='update', conditions={'method': ['PUT']})
315 m.connect('delete_users_group', '/user_groups/{user_group_id}',
302 m.connect('delete_users_group', '/user_groups/{user_group_id}',
316 action='delete', conditions={'method': ['DELETE']})
303 action='delete', conditions={'method': ['DELETE']})
317 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
304 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
318 action='edit', conditions={'method': ['GET']},
305 action='edit', conditions={'method': ['GET']},
319 function=check_user_group)
306 function=check_user_group)
320
307
321 # EXTRAS USER GROUP ROUTES
308 # EXTRAS USER GROUP ROUTES
322 m.connect('edit_user_group_global_perms',
309 m.connect('edit_user_group_global_perms',
323 '/user_groups/{user_group_id}/edit/global_permissions',
310 '/user_groups/{user_group_id}/edit/global_permissions',
324 action='edit_global_perms', conditions={'method': ['GET']})
311 action='edit_global_perms', conditions={'method': ['GET']})
325 m.connect('edit_user_group_global_perms',
312 m.connect('edit_user_group_global_perms',
326 '/user_groups/{user_group_id}/edit/global_permissions',
313 '/user_groups/{user_group_id}/edit/global_permissions',
327 action='update_global_perms', conditions={'method': ['PUT']})
314 action='update_global_perms', conditions={'method': ['PUT']})
328 m.connect('edit_user_group_perms_summary',
315 m.connect('edit_user_group_perms_summary',
329 '/user_groups/{user_group_id}/edit/permissions_summary',
316 '/user_groups/{user_group_id}/edit/permissions_summary',
330 action='edit_perms_summary', conditions={'method': ['GET']})
317 action='edit_perms_summary', conditions={'method': ['GET']})
331
318
332 m.connect('edit_user_group_perms',
319 m.connect('edit_user_group_perms',
333 '/user_groups/{user_group_id}/edit/permissions',
320 '/user_groups/{user_group_id}/edit/permissions',
334 action='edit_perms', conditions={'method': ['GET']})
321 action='edit_perms', conditions={'method': ['GET']})
335 m.connect('edit_user_group_perms',
322 m.connect('edit_user_group_perms',
336 '/user_groups/{user_group_id}/edit/permissions',
323 '/user_groups/{user_group_id}/edit/permissions',
337 action='update_perms', conditions={'method': ['PUT']})
324 action='update_perms', conditions={'method': ['PUT']})
338
325
339 m.connect('edit_user_group_advanced',
326 m.connect('edit_user_group_advanced',
340 '/user_groups/{user_group_id}/edit/advanced',
327 '/user_groups/{user_group_id}/edit/advanced',
341 action='edit_advanced', conditions={'method': ['GET']})
328 action='edit_advanced', conditions={'method': ['GET']})
342
329
343 m.connect('edit_user_group_advanced_sync',
330 m.connect('edit_user_group_advanced_sync',
344 '/user_groups/{user_group_id}/edit/advanced/sync',
331 '/user_groups/{user_group_id}/edit/advanced/sync',
345 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
332 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
346
333
347 m.connect('edit_user_group_members',
334 m.connect('edit_user_group_members',
348 '/user_groups/{user_group_id}/edit/members', jsroute=True,
335 '/user_groups/{user_group_id}/edit/members', jsroute=True,
349 action='user_group_members', conditions={'method': ['GET']})
336 action='user_group_members', conditions={'method': ['GET']})
350
337
351 # ADMIN PERMISSIONS ROUTES
338 # ADMIN PERMISSIONS ROUTES
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 controller='admin/permissions') as m:
340 controller='admin/permissions') as m:
354 m.connect('admin_permissions_application', '/permissions/application',
341 m.connect('admin_permissions_application', '/permissions/application',
355 action='permission_application_update', conditions={'method': ['POST']})
342 action='permission_application_update', conditions={'method': ['POST']})
356 m.connect('admin_permissions_application', '/permissions/application',
343 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application', conditions={'method': ['GET']})
344 action='permission_application', conditions={'method': ['GET']})
358
345
359 m.connect('admin_permissions_global', '/permissions/global',
346 m.connect('admin_permissions_global', '/permissions/global',
360 action='permission_global_update', conditions={'method': ['POST']})
347 action='permission_global_update', conditions={'method': ['POST']})
361 m.connect('admin_permissions_global', '/permissions/global',
348 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global', conditions={'method': ['GET']})
349 action='permission_global', conditions={'method': ['GET']})
363
350
364 m.connect('admin_permissions_object', '/permissions/object',
351 m.connect('admin_permissions_object', '/permissions/object',
365 action='permission_objects_update', conditions={'method': ['POST']})
352 action='permission_objects_update', conditions={'method': ['POST']})
366 m.connect('admin_permissions_object', '/permissions/object',
353 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects', conditions={'method': ['GET']})
354 action='permission_objects', conditions={'method': ['GET']})
368
355
369 m.connect('admin_permissions_ips', '/permissions/ips',
356 m.connect('admin_permissions_ips', '/permissions/ips',
370 action='permission_ips', conditions={'method': ['POST']})
357 action='permission_ips', conditions={'method': ['POST']})
371 m.connect('admin_permissions_ips', '/permissions/ips',
358 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['GET']})
359 action='permission_ips', conditions={'method': ['GET']})
373
360
374 m.connect('admin_permissions_overview', '/permissions/overview',
361 m.connect('admin_permissions_overview', '/permissions/overview',
375 action='permission_perms', conditions={'method': ['GET']})
362 action='permission_perms', conditions={'method': ['GET']})
376
363
377 # ADMIN DEFAULTS REST ROUTES
364 # ADMIN DEFAULTS REST ROUTES
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
365 with rmap.submapper(path_prefix=ADMIN_PREFIX,
379 controller='admin/defaults') as m:
366 controller='admin/defaults') as m:
380 m.connect('admin_defaults_repositories', '/defaults/repositories',
367 m.connect('admin_defaults_repositories', '/defaults/repositories',
381 action='update_repository_defaults', conditions={'method': ['POST']})
368 action='update_repository_defaults', conditions={'method': ['POST']})
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
369 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='index', conditions={'method': ['GET']})
370 action='index', conditions={'method': ['GET']})
384
371
385 # ADMIN DEBUG STYLE ROUTES
372 # ADMIN DEBUG STYLE ROUTES
386 if str2bool(config.get('debug_style')):
373 if str2bool(config.get('debug_style')):
387 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
374 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
388 controller='debug_style') as m:
375 controller='debug_style') as m:
389 m.connect('debug_style_home', '',
376 m.connect('debug_style_home', '',
390 action='index', conditions={'method': ['GET']})
377 action='index', conditions={'method': ['GET']})
391 m.connect('debug_style_template', '/t/{t_path}',
378 m.connect('debug_style_template', '/t/{t_path}',
392 action='template', conditions={'method': ['GET']})
379 action='template', conditions={'method': ['GET']})
393
380
394 # ADMIN SETTINGS ROUTES
381 # ADMIN SETTINGS ROUTES
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
382 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 controller='admin/settings') as m:
383 controller='admin/settings') as m:
397
384
398 # default
385 # default
399 m.connect('admin_settings', '/settings',
386 m.connect('admin_settings', '/settings',
400 action='settings_global_update',
387 action='settings_global_update',
401 conditions={'method': ['POST']})
388 conditions={'method': ['POST']})
402 m.connect('admin_settings', '/settings',
389 m.connect('admin_settings', '/settings',
403 action='settings_global', conditions={'method': ['GET']})
390 action='settings_global', conditions={'method': ['GET']})
404
391
405 m.connect('admin_settings_vcs', '/settings/vcs',
392 m.connect('admin_settings_vcs', '/settings/vcs',
406 action='settings_vcs_update',
393 action='settings_vcs_update',
407 conditions={'method': ['POST']})
394 conditions={'method': ['POST']})
408 m.connect('admin_settings_vcs', '/settings/vcs',
395 m.connect('admin_settings_vcs', '/settings/vcs',
409 action='settings_vcs',
396 action='settings_vcs',
410 conditions={'method': ['GET']})
397 conditions={'method': ['GET']})
411 m.connect('admin_settings_vcs', '/settings/vcs',
398 m.connect('admin_settings_vcs', '/settings/vcs',
412 action='delete_svn_pattern',
399 action='delete_svn_pattern',
413 conditions={'method': ['DELETE']})
400 conditions={'method': ['DELETE']})
414
401
415 m.connect('admin_settings_mapping', '/settings/mapping',
402 m.connect('admin_settings_mapping', '/settings/mapping',
416 action='settings_mapping_update',
403 action='settings_mapping_update',
417 conditions={'method': ['POST']})
404 conditions={'method': ['POST']})
418 m.connect('admin_settings_mapping', '/settings/mapping',
405 m.connect('admin_settings_mapping', '/settings/mapping',
419 action='settings_mapping', conditions={'method': ['GET']})
406 action='settings_mapping', conditions={'method': ['GET']})
420
407
421 m.connect('admin_settings_global', '/settings/global',
408 m.connect('admin_settings_global', '/settings/global',
422 action='settings_global_update',
409 action='settings_global_update',
423 conditions={'method': ['POST']})
410 conditions={'method': ['POST']})
424 m.connect('admin_settings_global', '/settings/global',
411 m.connect('admin_settings_global', '/settings/global',
425 action='settings_global', conditions={'method': ['GET']})
412 action='settings_global', conditions={'method': ['GET']})
426
413
427 m.connect('admin_settings_visual', '/settings/visual',
414 m.connect('admin_settings_visual', '/settings/visual',
428 action='settings_visual_update',
415 action='settings_visual_update',
429 conditions={'method': ['POST']})
416 conditions={'method': ['POST']})
430 m.connect('admin_settings_visual', '/settings/visual',
417 m.connect('admin_settings_visual', '/settings/visual',
431 action='settings_visual', conditions={'method': ['GET']})
418 action='settings_visual', conditions={'method': ['GET']})
432
419
433 m.connect('admin_settings_issuetracker',
420 m.connect('admin_settings_issuetracker',
434 '/settings/issue-tracker', action='settings_issuetracker',
421 '/settings/issue-tracker', action='settings_issuetracker',
435 conditions={'method': ['GET']})
422 conditions={'method': ['GET']})
436 m.connect('admin_settings_issuetracker_save',
423 m.connect('admin_settings_issuetracker_save',
437 '/settings/issue-tracker/save',
424 '/settings/issue-tracker/save',
438 action='settings_issuetracker_save',
425 action='settings_issuetracker_save',
439 conditions={'method': ['POST']})
426 conditions={'method': ['POST']})
440 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
427 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
441 action='settings_issuetracker_test',
428 action='settings_issuetracker_test',
442 conditions={'method': ['POST']})
429 conditions={'method': ['POST']})
443 m.connect('admin_issuetracker_delete',
430 m.connect('admin_issuetracker_delete',
444 '/settings/issue-tracker/delete',
431 '/settings/issue-tracker/delete',
445 action='settings_issuetracker_delete',
432 action='settings_issuetracker_delete',
446 conditions={'method': ['DELETE']})
433 conditions={'method': ['DELETE']})
447
434
448 m.connect('admin_settings_email', '/settings/email',
435 m.connect('admin_settings_email', '/settings/email',
449 action='settings_email_update',
436 action='settings_email_update',
450 conditions={'method': ['POST']})
437 conditions={'method': ['POST']})
451 m.connect('admin_settings_email', '/settings/email',
438 m.connect('admin_settings_email', '/settings/email',
452 action='settings_email', conditions={'method': ['GET']})
439 action='settings_email', conditions={'method': ['GET']})
453
440
454 m.connect('admin_settings_hooks', '/settings/hooks',
441 m.connect('admin_settings_hooks', '/settings/hooks',
455 action='settings_hooks_update',
442 action='settings_hooks_update',
456 conditions={'method': ['POST', 'DELETE']})
443 conditions={'method': ['POST', 'DELETE']})
457 m.connect('admin_settings_hooks', '/settings/hooks',
444 m.connect('admin_settings_hooks', '/settings/hooks',
458 action='settings_hooks', conditions={'method': ['GET']})
445 action='settings_hooks', conditions={'method': ['GET']})
459
446
460 m.connect('admin_settings_search', '/settings/search',
447 m.connect('admin_settings_search', '/settings/search',
461 action='settings_search', conditions={'method': ['GET']})
448 action='settings_search', conditions={'method': ['GET']})
462
449
463 m.connect('admin_settings_supervisor', '/settings/supervisor',
450 m.connect('admin_settings_supervisor', '/settings/supervisor',
464 action='settings_supervisor', conditions={'method': ['GET']})
451 action='settings_supervisor', conditions={'method': ['GET']})
465 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
452 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
466 action='settings_supervisor_log', conditions={'method': ['GET']})
453 action='settings_supervisor_log', conditions={'method': ['GET']})
467
454
468 m.connect('admin_settings_labs', '/settings/labs',
455 m.connect('admin_settings_labs', '/settings/labs',
469 action='settings_labs_update',
456 action='settings_labs_update',
470 conditions={'method': ['POST']})
457 conditions={'method': ['POST']})
471 m.connect('admin_settings_labs', '/settings/labs',
458 m.connect('admin_settings_labs', '/settings/labs',
472 action='settings_labs', conditions={'method': ['GET']})
459 action='settings_labs', conditions={'method': ['GET']})
473
460
474 # ADMIN MY ACCOUNT
461 # ADMIN MY ACCOUNT
475 with rmap.submapper(path_prefix=ADMIN_PREFIX,
462 with rmap.submapper(path_prefix=ADMIN_PREFIX,
476 controller='admin/my_account') as m:
463 controller='admin/my_account') as m:
477
464
478 m.connect('my_account_edit', '/my_account/edit',
465 m.connect('my_account_edit', '/my_account/edit',
479 action='my_account_edit', conditions={'method': ['GET']})
466 action='my_account_edit', conditions={'method': ['GET']})
480 m.connect('my_account', '/my_account/update',
467 m.connect('my_account', '/my_account/update',
481 action='my_account_update', conditions={'method': ['POST']})
468 action='my_account_update', conditions={'method': ['POST']})
482
469
483 # NOTE(marcink): this needs to be kept for password force flag to be
470 # NOTE(marcink): this needs to be kept for password force flag to be
484 # handler, remove after migration to pyramid
471 # handler, remove after migration to pyramid
485 m.connect('my_account_password', '/my_account/password',
472 m.connect('my_account_password', '/my_account/password',
486 action='my_account_password', conditions={'method': ['GET']})
473 action='my_account_password', conditions={'method': ['GET']})
487
474
488 m.connect('my_account_pullrequests', '/my_account/pull_requests',
475 m.connect('my_account_pullrequests', '/my_account/pull_requests',
489 action='my_account_pullrequests', conditions={'method': ['GET']})
476 action='my_account_pullrequests', conditions={'method': ['GET']})
490
477
491 # NOTIFICATION REST ROUTES
478 # NOTIFICATION REST ROUTES
492 with rmap.submapper(path_prefix=ADMIN_PREFIX,
479 with rmap.submapper(path_prefix=ADMIN_PREFIX,
493 controller='admin/notifications') as m:
480 controller='admin/notifications') as m:
494 m.connect('notifications', '/notifications',
481 m.connect('notifications', '/notifications',
495 action='index', conditions={'method': ['GET']})
482 action='index', conditions={'method': ['GET']})
496 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
483 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
497 action='mark_all_read', conditions={'method': ['POST']})
484 action='mark_all_read', conditions={'method': ['POST']})
498 m.connect('/notifications/{notification_id}',
485 m.connect('/notifications/{notification_id}',
499 action='update', conditions={'method': ['PUT']})
486 action='update', conditions={'method': ['PUT']})
500 m.connect('/notifications/{notification_id}',
487 m.connect('/notifications/{notification_id}',
501 action='delete', conditions={'method': ['DELETE']})
488 action='delete', conditions={'method': ['DELETE']})
502 m.connect('notification', '/notifications/{notification_id}',
489 m.connect('notification', '/notifications/{notification_id}',
503 action='show', conditions={'method': ['GET']})
490 action='show', conditions={'method': ['GET']})
504
491
505 # ADMIN GIST
492 # ADMIN GIST
506 with rmap.submapper(path_prefix=ADMIN_PREFIX,
493 with rmap.submapper(path_prefix=ADMIN_PREFIX,
507 controller='admin/gists') as m:
494 controller='admin/gists') as m:
508 m.connect('gists', '/gists',
495 m.connect('gists', '/gists',
509 action='create', conditions={'method': ['POST']})
496 action='create', conditions={'method': ['POST']})
510 m.connect('gists', '/gists', jsroute=True,
497 m.connect('gists', '/gists', jsroute=True,
511 action='index', conditions={'method': ['GET']})
498 action='index', conditions={'method': ['GET']})
512 m.connect('new_gist', '/gists/new', jsroute=True,
499 m.connect('new_gist', '/gists/new', jsroute=True,
513 action='new', conditions={'method': ['GET']})
500 action='new', conditions={'method': ['GET']})
514
501
515 m.connect('/gists/{gist_id}',
502 m.connect('/gists/{gist_id}',
516 action='delete', conditions={'method': ['DELETE']})
503 action='delete', conditions={'method': ['DELETE']})
517 m.connect('edit_gist', '/gists/{gist_id}/edit',
504 m.connect('edit_gist', '/gists/{gist_id}/edit',
518 action='edit_form', conditions={'method': ['GET']})
505 action='edit_form', conditions={'method': ['GET']})
519 m.connect('edit_gist', '/gists/{gist_id}/edit',
506 m.connect('edit_gist', '/gists/{gist_id}/edit',
520 action='edit', conditions={'method': ['POST']})
507 action='edit', conditions={'method': ['POST']})
521 m.connect(
508 m.connect(
522 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
509 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
523 action='check_revision', conditions={'method': ['GET']})
510 action='check_revision', conditions={'method': ['GET']})
524
511
525 m.connect('gist', '/gists/{gist_id}',
512 m.connect('gist', '/gists/{gist_id}',
526 action='show', conditions={'method': ['GET']})
513 action='show', conditions={'method': ['GET']})
527 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
514 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
528 revision='tip',
515 revision='tip',
529 action='show', conditions={'method': ['GET']})
516 action='show', conditions={'method': ['GET']})
530 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
517 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
531 revision='tip',
518 revision='tip',
532 action='show', conditions={'method': ['GET']})
519 action='show', conditions={'method': ['GET']})
533 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
520 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
534 revision='tip',
521 revision='tip',
535 action='show', conditions={'method': ['GET']},
522 action='show', conditions={'method': ['GET']},
536 requirements=URL_NAME_REQUIREMENTS)
523 requirements=URL_NAME_REQUIREMENTS)
537
524
538 # USER JOURNAL
525 # USER JOURNAL
539 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
526 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
540 controller='journal', action='index')
527 controller='journal', action='index')
541 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
528 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
542 controller='journal', action='journal_rss')
529 controller='journal', action='journal_rss')
543 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
530 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
544 controller='journal', action='journal_atom')
531 controller='journal', action='journal_atom')
545
532
546 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
533 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
547 controller='journal', action='public_journal')
534 controller='journal', action='public_journal')
548
535
549 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
536 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
550 controller='journal', action='public_journal_rss')
537 controller='journal', action='public_journal_rss')
551
538
552 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
539 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
553 controller='journal', action='public_journal_rss')
540 controller='journal', action='public_journal_rss')
554
541
555 rmap.connect('public_journal_atom',
542 rmap.connect('public_journal_atom',
556 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
543 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
557 action='public_journal_atom')
544 action='public_journal_atom')
558
545
559 rmap.connect('public_journal_atom_old',
546 rmap.connect('public_journal_atom_old',
560 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
547 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
561 action='public_journal_atom')
548 action='public_journal_atom')
562
549
563 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
550 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
564 controller='journal', action='toggle_following', jsroute=True,
551 controller='journal', action='toggle_following', jsroute=True,
565 conditions={'method': ['POST']})
552 conditions={'method': ['POST']})
566
553
567 # FEEDS
554 # FEEDS
568 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
555 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
569 controller='feed', action='rss',
556 controller='feed', action='rss',
570 conditions={'function': check_repo},
557 conditions={'function': check_repo},
571 requirements=URL_NAME_REQUIREMENTS)
558 requirements=URL_NAME_REQUIREMENTS)
572
559
573 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
560 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
574 controller='feed', action='atom',
561 controller='feed', action='atom',
575 conditions={'function': check_repo},
562 conditions={'function': check_repo},
576 requirements=URL_NAME_REQUIREMENTS)
563 requirements=URL_NAME_REQUIREMENTS)
577
564
578 #==========================================================================
565 #==========================================================================
579 # REPOSITORY ROUTES
566 # REPOSITORY ROUTES
580 #==========================================================================
567 #==========================================================================
581
568
582 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
569 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
583 controller='admin/repos', action='repo_creating',
570 controller='admin/repos', action='repo_creating',
584 requirements=URL_NAME_REQUIREMENTS)
571 requirements=URL_NAME_REQUIREMENTS)
585 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
572 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
586 controller='admin/repos', action='repo_check',
573 controller='admin/repos', action='repo_check',
587 requirements=URL_NAME_REQUIREMENTS)
574 requirements=URL_NAME_REQUIREMENTS)
588
575
589 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
576 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
590 controller='changeset', revision='tip',
577 controller='changeset', revision='tip',
591 conditions={'function': check_repo},
578 conditions={'function': check_repo},
592 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
579 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
593 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
580 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
594 controller='changeset', revision='tip', action='changeset_children',
581 controller='changeset', revision='tip', action='changeset_children',
595 conditions={'function': check_repo},
582 conditions={'function': check_repo},
596 requirements=URL_NAME_REQUIREMENTS)
583 requirements=URL_NAME_REQUIREMENTS)
597 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
584 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
598 controller='changeset', revision='tip', action='changeset_parents',
585 controller='changeset', revision='tip', action='changeset_parents',
599 conditions={'function': check_repo},
586 conditions={'function': check_repo},
600 requirements=URL_NAME_REQUIREMENTS)
587 requirements=URL_NAME_REQUIREMENTS)
601
588
602 # repo edit options
589 # repo edit options
603 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
590 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
604 controller='admin/repos', action='edit_fields',
591 controller='admin/repos', action='edit_fields',
605 conditions={'method': ['GET'], 'function': check_repo},
592 conditions={'method': ['GET'], 'function': check_repo},
606 requirements=URL_NAME_REQUIREMENTS)
593 requirements=URL_NAME_REQUIREMENTS)
607 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
594 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
608 controller='admin/repos', action='create_repo_field',
595 controller='admin/repos', action='create_repo_field',
609 conditions={'method': ['PUT'], 'function': check_repo},
596 conditions={'method': ['PUT'], 'function': check_repo},
610 requirements=URL_NAME_REQUIREMENTS)
597 requirements=URL_NAME_REQUIREMENTS)
611 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
598 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
612 controller='admin/repos', action='delete_repo_field',
599 controller='admin/repos', action='delete_repo_field',
613 conditions={'method': ['DELETE'], 'function': check_repo},
600 conditions={'method': ['DELETE'], 'function': check_repo},
614 requirements=URL_NAME_REQUIREMENTS)
601 requirements=URL_NAME_REQUIREMENTS)
615
602
616 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
603 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
617 controller='admin/repos', action='toggle_locking',
604 controller='admin/repos', action='toggle_locking',
618 conditions={'method': ['GET'], 'function': check_repo},
605 conditions={'method': ['GET'], 'function': check_repo},
619 requirements=URL_NAME_REQUIREMENTS)
606 requirements=URL_NAME_REQUIREMENTS)
620
607
621 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
608 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
622 controller='admin/repos', action='edit_remote_form',
609 controller='admin/repos', action='edit_remote_form',
623 conditions={'method': ['GET'], 'function': check_repo},
610 conditions={'method': ['GET'], 'function': check_repo},
624 requirements=URL_NAME_REQUIREMENTS)
611 requirements=URL_NAME_REQUIREMENTS)
625 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
612 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
626 controller='admin/repos', action='edit_remote',
613 controller='admin/repos', action='edit_remote',
627 conditions={'method': ['PUT'], 'function': check_repo},
614 conditions={'method': ['PUT'], 'function': check_repo},
628 requirements=URL_NAME_REQUIREMENTS)
615 requirements=URL_NAME_REQUIREMENTS)
629
616
630 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
617 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
631 controller='admin/repos', action='edit_statistics_form',
618 controller='admin/repos', action='edit_statistics_form',
632 conditions={'method': ['GET'], 'function': check_repo},
619 conditions={'method': ['GET'], 'function': check_repo},
633 requirements=URL_NAME_REQUIREMENTS)
620 requirements=URL_NAME_REQUIREMENTS)
634 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
621 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
635 controller='admin/repos', action='edit_statistics',
622 controller='admin/repos', action='edit_statistics',
636 conditions={'method': ['PUT'], 'function': check_repo},
623 conditions={'method': ['PUT'], 'function': check_repo},
637 requirements=URL_NAME_REQUIREMENTS)
624 requirements=URL_NAME_REQUIREMENTS)
638 rmap.connect('repo_settings_issuetracker',
625 rmap.connect('repo_settings_issuetracker',
639 '/{repo_name}/settings/issue-tracker',
626 '/{repo_name}/settings/issue-tracker',
640 controller='admin/repos', action='repo_issuetracker',
627 controller='admin/repos', action='repo_issuetracker',
641 conditions={'method': ['GET'], 'function': check_repo},
628 conditions={'method': ['GET'], 'function': check_repo},
642 requirements=URL_NAME_REQUIREMENTS)
629 requirements=URL_NAME_REQUIREMENTS)
643 rmap.connect('repo_issuetracker_test',
630 rmap.connect('repo_issuetracker_test',
644 '/{repo_name}/settings/issue-tracker/test',
631 '/{repo_name}/settings/issue-tracker/test',
645 controller='admin/repos', action='repo_issuetracker_test',
632 controller='admin/repos', action='repo_issuetracker_test',
646 conditions={'method': ['POST'], 'function': check_repo},
633 conditions={'method': ['POST'], 'function': check_repo},
647 requirements=URL_NAME_REQUIREMENTS)
634 requirements=URL_NAME_REQUIREMENTS)
648 rmap.connect('repo_issuetracker_delete',
635 rmap.connect('repo_issuetracker_delete',
649 '/{repo_name}/settings/issue-tracker/delete',
636 '/{repo_name}/settings/issue-tracker/delete',
650 controller='admin/repos', action='repo_issuetracker_delete',
637 controller='admin/repos', action='repo_issuetracker_delete',
651 conditions={'method': ['DELETE'], 'function': check_repo},
638 conditions={'method': ['DELETE'], 'function': check_repo},
652 requirements=URL_NAME_REQUIREMENTS)
639 requirements=URL_NAME_REQUIREMENTS)
653 rmap.connect('repo_issuetracker_save',
640 rmap.connect('repo_issuetracker_save',
654 '/{repo_name}/settings/issue-tracker/save',
641 '/{repo_name}/settings/issue-tracker/save',
655 controller='admin/repos', action='repo_issuetracker_save',
642 controller='admin/repos', action='repo_issuetracker_save',
656 conditions={'method': ['POST'], 'function': check_repo},
643 conditions={'method': ['POST'], 'function': check_repo},
657 requirements=URL_NAME_REQUIREMENTS)
644 requirements=URL_NAME_REQUIREMENTS)
658 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
645 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
659 controller='admin/repos', action='repo_settings_vcs_update',
646 controller='admin/repos', action='repo_settings_vcs_update',
660 conditions={'method': ['POST'], 'function': check_repo},
647 conditions={'method': ['POST'], 'function': check_repo},
661 requirements=URL_NAME_REQUIREMENTS)
648 requirements=URL_NAME_REQUIREMENTS)
662 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
649 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
663 controller='admin/repos', action='repo_settings_vcs',
650 controller='admin/repos', action='repo_settings_vcs',
664 conditions={'method': ['GET'], 'function': check_repo},
651 conditions={'method': ['GET'], 'function': check_repo},
665 requirements=URL_NAME_REQUIREMENTS)
652 requirements=URL_NAME_REQUIREMENTS)
666 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
653 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
667 controller='admin/repos', action='repo_delete_svn_pattern',
654 controller='admin/repos', action='repo_delete_svn_pattern',
668 conditions={'method': ['DELETE'], 'function': check_repo},
655 conditions={'method': ['DELETE'], 'function': check_repo},
669 requirements=URL_NAME_REQUIREMENTS)
656 requirements=URL_NAME_REQUIREMENTS)
670 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
657 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
671 controller='admin/repos', action='repo_settings_pullrequest',
658 controller='admin/repos', action='repo_settings_pullrequest',
672 conditions={'method': ['GET', 'POST'], 'function': check_repo},
659 conditions={'method': ['GET', 'POST'], 'function': check_repo},
673 requirements=URL_NAME_REQUIREMENTS)
660 requirements=URL_NAME_REQUIREMENTS)
674
661
675 # still working url for backward compat.
662 # still working url for backward compat.
676 rmap.connect('raw_changeset_home_depraced',
663 rmap.connect('raw_changeset_home_depraced',
677 '/{repo_name}/raw-changeset/{revision}',
664 '/{repo_name}/raw-changeset/{revision}',
678 controller='changeset', action='changeset_raw',
665 controller='changeset', action='changeset_raw',
679 revision='tip', conditions={'function': check_repo},
666 revision='tip', conditions={'function': check_repo},
680 requirements=URL_NAME_REQUIREMENTS)
667 requirements=URL_NAME_REQUIREMENTS)
681
668
682 # new URLs
669 # new URLs
683 rmap.connect('changeset_raw_home',
670 rmap.connect('changeset_raw_home',
684 '/{repo_name}/changeset-diff/{revision}',
671 '/{repo_name}/changeset-diff/{revision}',
685 controller='changeset', action='changeset_raw',
672 controller='changeset', action='changeset_raw',
686 revision='tip', conditions={'function': check_repo},
673 revision='tip', conditions={'function': check_repo},
687 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
688
675
689 rmap.connect('changeset_patch_home',
676 rmap.connect('changeset_patch_home',
690 '/{repo_name}/changeset-patch/{revision}',
677 '/{repo_name}/changeset-patch/{revision}',
691 controller='changeset', action='changeset_patch',
678 controller='changeset', action='changeset_patch',
692 revision='tip', conditions={'function': check_repo},
679 revision='tip', conditions={'function': check_repo},
693 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
694
681
695 rmap.connect('changeset_download_home',
682 rmap.connect('changeset_download_home',
696 '/{repo_name}/changeset-download/{revision}',
683 '/{repo_name}/changeset-download/{revision}',
697 controller='changeset', action='changeset_download',
684 controller='changeset', action='changeset_download',
698 revision='tip', conditions={'function': check_repo},
685 revision='tip', conditions={'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
686 requirements=URL_NAME_REQUIREMENTS)
700
687
701 rmap.connect('changeset_comment',
688 rmap.connect('changeset_comment',
702 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
689 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
703 controller='changeset', revision='tip', action='comment',
690 controller='changeset', revision='tip', action='comment',
704 conditions={'function': check_repo},
691 conditions={'function': check_repo},
705 requirements=URL_NAME_REQUIREMENTS)
692 requirements=URL_NAME_REQUIREMENTS)
706
693
707 rmap.connect('changeset_comment_preview',
694 rmap.connect('changeset_comment_preview',
708 '/{repo_name}/changeset/comment/preview', jsroute=True,
695 '/{repo_name}/changeset/comment/preview', jsroute=True,
709 controller='changeset', action='preview_comment',
696 controller='changeset', action='preview_comment',
710 conditions={'function': check_repo, 'method': ['POST']},
697 conditions={'function': check_repo, 'method': ['POST']},
711 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
712
699
713 rmap.connect('changeset_comment_delete',
700 rmap.connect('changeset_comment_delete',
714 '/{repo_name}/changeset/comment/{comment_id}/delete',
701 '/{repo_name}/changeset/comment/{comment_id}/delete',
715 controller='changeset', action='delete_comment',
702 controller='changeset', action='delete_comment',
716 conditions={'function': check_repo, 'method': ['DELETE']},
703 conditions={'function': check_repo, 'method': ['DELETE']},
717 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
704 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
718
705
719 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
706 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
720 controller='changeset', action='changeset_info',
707 controller='changeset', action='changeset_info',
721 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
708 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
722
709
723 rmap.connect('compare_home',
710 rmap.connect('compare_home',
724 '/{repo_name}/compare',
711 '/{repo_name}/compare',
725 controller='compare', action='index',
712 controller='compare', action='index',
726 conditions={'function': check_repo},
713 conditions={'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
714 requirements=URL_NAME_REQUIREMENTS)
728
715
729 rmap.connect('compare_url',
716 rmap.connect('compare_url',
730 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
717 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
731 controller='compare', action='compare',
718 controller='compare', action='compare',
732 conditions={'function': check_repo},
719 conditions={'function': check_repo},
733 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
720 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
734
721
735 rmap.connect('pullrequest_home',
722 rmap.connect('pullrequest_home',
736 '/{repo_name}/pull-request/new', controller='pullrequests',
723 '/{repo_name}/pull-request/new', controller='pullrequests',
737 action='index', conditions={'function': check_repo,
724 action='index', conditions={'function': check_repo,
738 'method': ['GET']},
725 'method': ['GET']},
739 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
726 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
740
727
741 rmap.connect('pullrequest',
728 rmap.connect('pullrequest',
742 '/{repo_name}/pull-request/new', controller='pullrequests',
729 '/{repo_name}/pull-request/new', controller='pullrequests',
743 action='create', conditions={'function': check_repo,
730 action='create', conditions={'function': check_repo,
744 'method': ['POST']},
731 'method': ['POST']},
745 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
732 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
746
733
747 rmap.connect('pullrequest_repo_refs',
734 rmap.connect('pullrequest_repo_refs',
748 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
735 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
749 controller='pullrequests',
736 controller='pullrequests',
750 action='get_repo_refs',
737 action='get_repo_refs',
751 conditions={'function': check_repo, 'method': ['GET']},
738 conditions={'function': check_repo, 'method': ['GET']},
752 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
739 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
753
740
754 rmap.connect('pullrequest_repo_destinations',
741 rmap.connect('pullrequest_repo_destinations',
755 '/{repo_name}/pull-request/repo-destinations',
742 '/{repo_name}/pull-request/repo-destinations',
756 controller='pullrequests',
743 controller='pullrequests',
757 action='get_repo_destinations',
744 action='get_repo_destinations',
758 conditions={'function': check_repo, 'method': ['GET']},
745 conditions={'function': check_repo, 'method': ['GET']},
759 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
746 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
760
747
761 rmap.connect('pullrequest_show',
748 rmap.connect('pullrequest_show',
762 '/{repo_name}/pull-request/{pull_request_id}',
749 '/{repo_name}/pull-request/{pull_request_id}',
763 controller='pullrequests',
750 controller='pullrequests',
764 action='show', conditions={'function': check_repo,
751 action='show', conditions={'function': check_repo,
765 'method': ['GET']},
752 'method': ['GET']},
766 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
753 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
767
754
768 rmap.connect('pullrequest_update',
755 rmap.connect('pullrequest_update',
769 '/{repo_name}/pull-request/{pull_request_id}',
756 '/{repo_name}/pull-request/{pull_request_id}',
770 controller='pullrequests',
757 controller='pullrequests',
771 action='update', conditions={'function': check_repo,
758 action='update', conditions={'function': check_repo,
772 'method': ['PUT']},
759 'method': ['PUT']},
773 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
760 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
774
761
775 rmap.connect('pullrequest_merge',
762 rmap.connect('pullrequest_merge',
776 '/{repo_name}/pull-request/{pull_request_id}',
763 '/{repo_name}/pull-request/{pull_request_id}',
777 controller='pullrequests',
764 controller='pullrequests',
778 action='merge', conditions={'function': check_repo,
765 action='merge', conditions={'function': check_repo,
779 'method': ['POST']},
766 'method': ['POST']},
780 requirements=URL_NAME_REQUIREMENTS)
767 requirements=URL_NAME_REQUIREMENTS)
781
768
782 rmap.connect('pullrequest_delete',
769 rmap.connect('pullrequest_delete',
783 '/{repo_name}/pull-request/{pull_request_id}',
770 '/{repo_name}/pull-request/{pull_request_id}',
784 controller='pullrequests',
771 controller='pullrequests',
785 action='delete', conditions={'function': check_repo,
772 action='delete', conditions={'function': check_repo,
786 'method': ['DELETE']},
773 'method': ['DELETE']},
787 requirements=URL_NAME_REQUIREMENTS)
774 requirements=URL_NAME_REQUIREMENTS)
788
775
789 rmap.connect('pullrequest_comment',
776 rmap.connect('pullrequest_comment',
790 '/{repo_name}/pull-request-comment/{pull_request_id}',
777 '/{repo_name}/pull-request-comment/{pull_request_id}',
791 controller='pullrequests',
778 controller='pullrequests',
792 action='comment', conditions={'function': check_repo,
779 action='comment', conditions={'function': check_repo,
793 'method': ['POST']},
780 'method': ['POST']},
794 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
781 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
795
782
796 rmap.connect('pullrequest_comment_delete',
783 rmap.connect('pullrequest_comment_delete',
797 '/{repo_name}/pull-request-comment/{comment_id}/delete',
784 '/{repo_name}/pull-request-comment/{comment_id}/delete',
798 controller='pullrequests', action='delete_comment',
785 controller='pullrequests', action='delete_comment',
799 conditions={'function': check_repo, 'method': ['DELETE']},
786 conditions={'function': check_repo, 'method': ['DELETE']},
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
787 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
801
788
802 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
789 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
803 controller='changelog', conditions={'function': check_repo},
790 controller='changelog', conditions={'function': check_repo},
804 requirements=URL_NAME_REQUIREMENTS)
791 requirements=URL_NAME_REQUIREMENTS)
805
792
806 rmap.connect('changelog_file_home',
793 rmap.connect('changelog_file_home',
807 '/{repo_name}/changelog/{revision}/{f_path}',
794 '/{repo_name}/changelog/{revision}/{f_path}',
808 controller='changelog', f_path=None,
795 controller='changelog', f_path=None,
809 conditions={'function': check_repo},
796 conditions={'function': check_repo},
810 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
797 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
811
798
812 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
799 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
813 controller='changelog', action='changelog_elements',
800 controller='changelog', action='changelog_elements',
814 conditions={'function': check_repo},
801 conditions={'function': check_repo},
815 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
802 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
816
803
817 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
804 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
818 controller='files', revision='tip', f_path='',
805 controller='files', revision='tip', f_path='',
819 conditions={'function': check_repo},
806 conditions={'function': check_repo},
820 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
821
808
822 rmap.connect('files_home_simple_catchrev',
809 rmap.connect('files_home_simple_catchrev',
823 '/{repo_name}/files/{revision}',
810 '/{repo_name}/files/{revision}',
824 controller='files', revision='tip', f_path='',
811 controller='files', revision='tip', f_path='',
825 conditions={'function': check_repo},
812 conditions={'function': check_repo},
826 requirements=URL_NAME_REQUIREMENTS)
813 requirements=URL_NAME_REQUIREMENTS)
827
814
828 rmap.connect('files_home_simple_catchall',
815 rmap.connect('files_home_simple_catchall',
829 '/{repo_name}/files',
816 '/{repo_name}/files',
830 controller='files', revision='tip', f_path='',
817 controller='files', revision='tip', f_path='',
831 conditions={'function': check_repo},
818 conditions={'function': check_repo},
832 requirements=URL_NAME_REQUIREMENTS)
819 requirements=URL_NAME_REQUIREMENTS)
833
820
834 rmap.connect('files_history_home',
821 rmap.connect('files_history_home',
835 '/{repo_name}/history/{revision}/{f_path}',
822 '/{repo_name}/history/{revision}/{f_path}',
836 controller='files', action='history', revision='tip', f_path='',
823 controller='files', action='history', revision='tip', f_path='',
837 conditions={'function': check_repo},
824 conditions={'function': check_repo},
838 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
825 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
839
826
840 rmap.connect('files_authors_home',
827 rmap.connect('files_authors_home',
841 '/{repo_name}/authors/{revision}/{f_path}',
828 '/{repo_name}/authors/{revision}/{f_path}',
842 controller='files', action='authors', revision='tip', f_path='',
829 controller='files', action='authors', revision='tip', f_path='',
843 conditions={'function': check_repo},
830 conditions={'function': check_repo},
844 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
831 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
845
832
846 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
833 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
847 controller='files', action='diff', f_path='',
834 controller='files', action='diff', f_path='',
848 conditions={'function': check_repo},
835 conditions={'function': check_repo},
849 requirements=URL_NAME_REQUIREMENTS)
836 requirements=URL_NAME_REQUIREMENTS)
850
837
851 rmap.connect('files_diff_2way_home',
838 rmap.connect('files_diff_2way_home',
852 '/{repo_name}/diff-2way/{f_path}',
839 '/{repo_name}/diff-2way/{f_path}',
853 controller='files', action='diff_2way', f_path='',
840 controller='files', action='diff_2way', f_path='',
854 conditions={'function': check_repo},
841 conditions={'function': check_repo},
855 requirements=URL_NAME_REQUIREMENTS)
842 requirements=URL_NAME_REQUIREMENTS)
856
843
857 rmap.connect('files_rawfile_home',
844 rmap.connect('files_rawfile_home',
858 '/{repo_name}/rawfile/{revision}/{f_path}',
845 '/{repo_name}/rawfile/{revision}/{f_path}',
859 controller='files', action='rawfile', revision='tip',
846 controller='files', action='rawfile', revision='tip',
860 f_path='', conditions={'function': check_repo},
847 f_path='', conditions={'function': check_repo},
861 requirements=URL_NAME_REQUIREMENTS)
848 requirements=URL_NAME_REQUIREMENTS)
862
849
863 rmap.connect('files_raw_home',
850 rmap.connect('files_raw_home',
864 '/{repo_name}/raw/{revision}/{f_path}',
851 '/{repo_name}/raw/{revision}/{f_path}',
865 controller='files', action='raw', revision='tip', f_path='',
852 controller='files', action='raw', revision='tip', f_path='',
866 conditions={'function': check_repo},
853 conditions={'function': check_repo},
867 requirements=URL_NAME_REQUIREMENTS)
854 requirements=URL_NAME_REQUIREMENTS)
868
855
869 rmap.connect('files_render_home',
856 rmap.connect('files_render_home',
870 '/{repo_name}/render/{revision}/{f_path}',
857 '/{repo_name}/render/{revision}/{f_path}',
871 controller='files', action='index', revision='tip', f_path='',
858 controller='files', action='index', revision='tip', f_path='',
872 rendered=True, conditions={'function': check_repo},
859 rendered=True, conditions={'function': check_repo},
873 requirements=URL_NAME_REQUIREMENTS)
860 requirements=URL_NAME_REQUIREMENTS)
874
861
875 rmap.connect('files_annotate_home',
862 rmap.connect('files_annotate_home',
876 '/{repo_name}/annotate/{revision}/{f_path}',
863 '/{repo_name}/annotate/{revision}/{f_path}',
877 controller='files', action='index', revision='tip',
864 controller='files', action='index', revision='tip',
878 f_path='', annotate=True, conditions={'function': check_repo},
865 f_path='', annotate=True, conditions={'function': check_repo},
879 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
866 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
880
867
881 rmap.connect('files_annotate_previous',
868 rmap.connect('files_annotate_previous',
882 '/{repo_name}/annotate-previous/{revision}/{f_path}',
869 '/{repo_name}/annotate-previous/{revision}/{f_path}',
883 controller='files', action='annotate_previous', revision='tip',
870 controller='files', action='annotate_previous', revision='tip',
884 f_path='', annotate=True, conditions={'function': check_repo},
871 f_path='', annotate=True, conditions={'function': check_repo},
885 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
872 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
886
873
887 rmap.connect('files_edit',
874 rmap.connect('files_edit',
888 '/{repo_name}/edit/{revision}/{f_path}',
875 '/{repo_name}/edit/{revision}/{f_path}',
889 controller='files', action='edit', revision='tip',
876 controller='files', action='edit', revision='tip',
890 f_path='',
877 f_path='',
891 conditions={'function': check_repo, 'method': ['POST']},
878 conditions={'function': check_repo, 'method': ['POST']},
892 requirements=URL_NAME_REQUIREMENTS)
879 requirements=URL_NAME_REQUIREMENTS)
893
880
894 rmap.connect('files_edit_home',
881 rmap.connect('files_edit_home',
895 '/{repo_name}/edit/{revision}/{f_path}',
882 '/{repo_name}/edit/{revision}/{f_path}',
896 controller='files', action='edit_home', revision='tip',
883 controller='files', action='edit_home', revision='tip',
897 f_path='', conditions={'function': check_repo},
884 f_path='', conditions={'function': check_repo},
898 requirements=URL_NAME_REQUIREMENTS)
885 requirements=URL_NAME_REQUIREMENTS)
899
886
900 rmap.connect('files_add',
887 rmap.connect('files_add',
901 '/{repo_name}/add/{revision}/{f_path}',
888 '/{repo_name}/add/{revision}/{f_path}',
902 controller='files', action='add', revision='tip',
889 controller='files', action='add', revision='tip',
903 f_path='',
890 f_path='',
904 conditions={'function': check_repo, 'method': ['POST']},
891 conditions={'function': check_repo, 'method': ['POST']},
905 requirements=URL_NAME_REQUIREMENTS)
892 requirements=URL_NAME_REQUIREMENTS)
906
893
907 rmap.connect('files_add_home',
894 rmap.connect('files_add_home',
908 '/{repo_name}/add/{revision}/{f_path}',
895 '/{repo_name}/add/{revision}/{f_path}',
909 controller='files', action='add_home', revision='tip',
896 controller='files', action='add_home', revision='tip',
910 f_path='', conditions={'function': check_repo},
897 f_path='', conditions={'function': check_repo},
911 requirements=URL_NAME_REQUIREMENTS)
898 requirements=URL_NAME_REQUIREMENTS)
912
899
913 rmap.connect('files_delete',
900 rmap.connect('files_delete',
914 '/{repo_name}/delete/{revision}/{f_path}',
901 '/{repo_name}/delete/{revision}/{f_path}',
915 controller='files', action='delete', revision='tip',
902 controller='files', action='delete', revision='tip',
916 f_path='',
903 f_path='',
917 conditions={'function': check_repo, 'method': ['POST']},
904 conditions={'function': check_repo, 'method': ['POST']},
918 requirements=URL_NAME_REQUIREMENTS)
905 requirements=URL_NAME_REQUIREMENTS)
919
906
920 rmap.connect('files_delete_home',
907 rmap.connect('files_delete_home',
921 '/{repo_name}/delete/{revision}/{f_path}',
908 '/{repo_name}/delete/{revision}/{f_path}',
922 controller='files', action='delete_home', revision='tip',
909 controller='files', action='delete_home', revision='tip',
923 f_path='', conditions={'function': check_repo},
910 f_path='', conditions={'function': check_repo},
924 requirements=URL_NAME_REQUIREMENTS)
911 requirements=URL_NAME_REQUIREMENTS)
925
912
926 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
913 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
927 controller='files', action='archivefile',
914 controller='files', action='archivefile',
928 conditions={'function': check_repo},
915 conditions={'function': check_repo},
929 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
916 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
930
917
931 rmap.connect('files_nodelist_home',
918 rmap.connect('files_nodelist_home',
932 '/{repo_name}/nodelist/{revision}/{f_path}',
919 '/{repo_name}/nodelist/{revision}/{f_path}',
933 controller='files', action='nodelist',
920 controller='files', action='nodelist',
934 conditions={'function': check_repo},
921 conditions={'function': check_repo},
935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
922 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
936
923
937 rmap.connect('files_nodetree_full',
924 rmap.connect('files_nodetree_full',
938 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
925 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
939 controller='files', action='nodetree_full',
926 controller='files', action='nodetree_full',
940 conditions={'function': check_repo},
927 conditions={'function': check_repo},
941 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
928 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
942
929
943 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
930 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
944 controller='forks', action='fork_create',
931 controller='forks', action='fork_create',
945 conditions={'function': check_repo, 'method': ['POST']},
932 conditions={'function': check_repo, 'method': ['POST']},
946 requirements=URL_NAME_REQUIREMENTS)
933 requirements=URL_NAME_REQUIREMENTS)
947
934
948 rmap.connect('repo_fork_home', '/{repo_name}/fork',
935 rmap.connect('repo_fork_home', '/{repo_name}/fork',
949 controller='forks', action='fork',
936 controller='forks', action='fork',
950 conditions={'function': check_repo},
937 conditions={'function': check_repo},
951 requirements=URL_NAME_REQUIREMENTS)
938 requirements=URL_NAME_REQUIREMENTS)
952
939
953 rmap.connect('repo_forks_home', '/{repo_name}/forks',
940 rmap.connect('repo_forks_home', '/{repo_name}/forks',
954 controller='forks', action='forks',
941 controller='forks', action='forks',
955 conditions={'function': check_repo},
942 conditions={'function': check_repo},
956 requirements=URL_NAME_REQUIREMENTS)
943 requirements=URL_NAME_REQUIREMENTS)
957
944
958 return rmap
945 return rmap
@@ -1,643 +1,493 b''
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 Users crud controller for pylons
22 Users crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 from formencode import htmlfill
28 from formencode import htmlfill
29 from pylons import request, tmpl_context as c, url, config
29 from pylons import request, tmpl_context as c, url, config
30 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib import auth
36 from rhodecode.lib import auth
37 from rhodecode.lib import audit_logger
37 from rhodecode.lib import audit_logger
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator, AuthUser)
39 LoginRequired, HasPermissionAllDecorator, AuthUser)
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.exceptions import (
41 from rhodecode.lib.exceptions import (
42 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
42 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
43 UserOwnsUserGroupsException, UserCreationError)
43 UserOwnsUserGroupsException, UserCreationError)
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
45
45
46 from rhodecode.model.db import (
46 from rhodecode.model.db import (
47 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
47 PullRequestReviewers, User, UserEmailMap, UserIpMap, RepoGroup)
48 from rhodecode.model.forms import (
48 from rhodecode.model.forms import (
49 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
49 UserForm, UserPermissionsForm, UserIndividualPermissionsForm)
50 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.meta import Session
52 from rhodecode.model.meta import Session
53 from rhodecode.model.permission import PermissionModel
53 from rhodecode.model.permission import PermissionModel
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class UsersController(BaseController):
58 class UsersController(BaseController):
59 """REST Controller styled on the Atom Publishing Protocol"""
59 """REST Controller styled on the Atom Publishing Protocol"""
60
60
61 @LoginRequired()
61 @LoginRequired()
62 def __before__(self):
62 def __before__(self):
63 super(UsersController, self).__before__()
63 super(UsersController, self).__before__()
64 c.available_permissions = config['available_permissions']
64 c.available_permissions = config['available_permissions']
65 c.allowed_languages = [
65 c.allowed_languages = [
66 ('en', 'English (en)'),
66 ('en', 'English (en)'),
67 ('de', 'German (de)'),
67 ('de', 'German (de)'),
68 ('fr', 'French (fr)'),
68 ('fr', 'French (fr)'),
69 ('it', 'Italian (it)'),
69 ('it', 'Italian (it)'),
70 ('ja', 'Japanese (ja)'),
70 ('ja', 'Japanese (ja)'),
71 ('pl', 'Polish (pl)'),
71 ('pl', 'Polish (pl)'),
72 ('pt', 'Portuguese (pt)'),
72 ('pt', 'Portuguese (pt)'),
73 ('ru', 'Russian (ru)'),
73 ('ru', 'Russian (ru)'),
74 ('zh', 'Chinese (zh)'),
74 ('zh', 'Chinese (zh)'),
75 ]
75 ]
76 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
76 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
77
77
78 def _get_personal_repo_group_template_vars(self):
78 def _get_personal_repo_group_template_vars(self):
79 DummyUser = AttributeDict({
79 DummyUser = AttributeDict({
80 'username': '${username}',
80 'username': '${username}',
81 'user_id': '${user_id}',
81 'user_id': '${user_id}',
82 })
82 })
83 c.default_create_repo_group = RepoGroupModel() \
83 c.default_create_repo_group = RepoGroupModel() \
84 .get_default_create_personal_repo_group()
84 .get_default_create_personal_repo_group()
85 c.personal_repo_group_name = RepoGroupModel() \
85 c.personal_repo_group_name = RepoGroupModel() \
86 .get_personal_group_name(DummyUser)
86 .get_personal_group_name(DummyUser)
87
87
88 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
89 @auth.CSRFRequired()
89 @auth.CSRFRequired()
90 def create(self):
90 def create(self):
91 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
91 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
92 user_model = UserModel()
92 user_model = UserModel()
93 user_form = UserForm()()
93 user_form = UserForm()()
94 try:
94 try:
95 form_result = user_form.to_python(dict(request.POST))
95 form_result = user_form.to_python(dict(request.POST))
96 user = user_model.create(form_result)
96 user = user_model.create(form_result)
97 Session().flush()
97 Session().flush()
98 creation_data = user.get_api_data()
98 creation_data = user.get_api_data()
99 username = form_result['username']
99 username = form_result['username']
100
100
101 audit_logger.store_web(
101 audit_logger.store_web(
102 'user.create', action_data={'data': creation_data},
102 'user.create', action_data={'data': creation_data},
103 user=c.rhodecode_user)
103 user=c.rhodecode_user)
104
104
105 user_link = h.link_to(h.escape(username),
105 user_link = h.link_to(h.escape(username),
106 url('edit_user',
106 url('edit_user',
107 user_id=user.user_id))
107 user_id=user.user_id))
108 h.flash(h.literal(_('Created user %(user_link)s')
108 h.flash(h.literal(_('Created user %(user_link)s')
109 % {'user_link': user_link}), category='success')
109 % {'user_link': user_link}), category='success')
110 Session().commit()
110 Session().commit()
111 except formencode.Invalid as errors:
111 except formencode.Invalid as errors:
112 self._get_personal_repo_group_template_vars()
112 self._get_personal_repo_group_template_vars()
113 return htmlfill.render(
113 return htmlfill.render(
114 render('admin/users/user_add.mako'),
114 render('admin/users/user_add.mako'),
115 defaults=errors.value,
115 defaults=errors.value,
116 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
117 prefix_error=False,
117 prefix_error=False,
118 encoding="UTF-8",
118 encoding="UTF-8",
119 force_defaults=False)
119 force_defaults=False)
120 except UserCreationError as e:
120 except UserCreationError as e:
121 h.flash(e, 'error')
121 h.flash(e, 'error')
122 except Exception:
122 except Exception:
123 log.exception("Exception creation of user")
123 log.exception("Exception creation of user")
124 h.flash(_('Error occurred during creation of user %s')
124 h.flash(_('Error occurred during creation of user %s')
125 % request.POST.get('username'), category='error')
125 % request.POST.get('username'), category='error')
126 return redirect(h.route_path('users'))
126 return redirect(h.route_path('users'))
127
127
128 @HasPermissionAllDecorator('hg.admin')
128 @HasPermissionAllDecorator('hg.admin')
129 def new(self):
129 def new(self):
130 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
130 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
131 self._get_personal_repo_group_template_vars()
131 self._get_personal_repo_group_template_vars()
132 return render('admin/users/user_add.mako')
132 return render('admin/users/user_add.mako')
133
133
134 @HasPermissionAllDecorator('hg.admin')
134 @HasPermissionAllDecorator('hg.admin')
135 @auth.CSRFRequired()
135 @auth.CSRFRequired()
136 def update(self, user_id):
136 def update(self, user_id):
137
137
138 user_id = safe_int(user_id)
138 user_id = safe_int(user_id)
139 c.user = User.get_or_404(user_id)
139 c.user = User.get_or_404(user_id)
140 c.active = 'profile'
140 c.active = 'profile'
141 c.extern_type = c.user.extern_type
141 c.extern_type = c.user.extern_type
142 c.extern_name = c.user.extern_name
142 c.extern_name = c.user.extern_name
143 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
143 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
144 available_languages = [x[0] for x in c.allowed_languages]
144 available_languages = [x[0] for x in c.allowed_languages]
145 _form = UserForm(edit=True, available_languages=available_languages,
145 _form = UserForm(edit=True, available_languages=available_languages,
146 old_data={'user_id': user_id,
146 old_data={'user_id': user_id,
147 'email': c.user.email})()
147 'email': c.user.email})()
148 form_result = {}
148 form_result = {}
149 old_values = c.user.get_api_data()
149 old_values = c.user.get_api_data()
150 try:
150 try:
151 form_result = _form.to_python(dict(request.POST))
151 form_result = _form.to_python(dict(request.POST))
152 skip_attrs = ['extern_type', 'extern_name']
152 skip_attrs = ['extern_type', 'extern_name']
153 # TODO: plugin should define if username can be updated
153 # TODO: plugin should define if username can be updated
154 if c.extern_type != "rhodecode":
154 if c.extern_type != "rhodecode":
155 # forbid updating username for external accounts
155 # forbid updating username for external accounts
156 skip_attrs.append('username')
156 skip_attrs.append('username')
157
157
158 UserModel().update_user(
158 UserModel().update_user(
159 user_id, skip_attrs=skip_attrs, **form_result)
159 user_id, skip_attrs=skip_attrs, **form_result)
160
160
161 audit_logger.store_web(
161 audit_logger.store_web(
162 'user.edit', action_data={'old_data': old_values},
162 'user.edit', action_data={'old_data': old_values},
163 user=c.rhodecode_user)
163 user=c.rhodecode_user)
164
164
165 Session().commit()
165 Session().commit()
166 h.flash(_('User updated successfully'), category='success')
166 h.flash(_('User updated successfully'), category='success')
167 except formencode.Invalid as errors:
167 except formencode.Invalid as errors:
168 defaults = errors.value
168 defaults = errors.value
169 e = errors.error_dict or {}
169 e = errors.error_dict or {}
170
170
171 return htmlfill.render(
171 return htmlfill.render(
172 render('admin/users/user_edit.mako'),
172 render('admin/users/user_edit.mako'),
173 defaults=defaults,
173 defaults=defaults,
174 errors=e,
174 errors=e,
175 prefix_error=False,
175 prefix_error=False,
176 encoding="UTF-8",
176 encoding="UTF-8",
177 force_defaults=False)
177 force_defaults=False)
178 except UserCreationError as e:
178 except UserCreationError as e:
179 h.flash(e, 'error')
179 h.flash(e, 'error')
180 except Exception:
180 except Exception:
181 log.exception("Exception updating user")
181 log.exception("Exception updating user")
182 h.flash(_('Error occurred during update of user %s')
182 h.flash(_('Error occurred during update of user %s')
183 % form_result.get('username'), category='error')
183 % form_result.get('username'), category='error')
184 return redirect(url('edit_user', user_id=user_id))
184 return redirect(url('edit_user', user_id=user_id))
185
185
186 @HasPermissionAllDecorator('hg.admin')
186 @HasPermissionAllDecorator('hg.admin')
187 @auth.CSRFRequired()
187 @auth.CSRFRequired()
188 def delete(self, user_id):
188 def delete(self, user_id):
189 user_id = safe_int(user_id)
189 user_id = safe_int(user_id)
190 c.user = User.get_or_404(user_id)
190 c.user = User.get_or_404(user_id)
191
191
192 _repos = c.user.repositories
192 _repos = c.user.repositories
193 _repo_groups = c.user.repository_groups
193 _repo_groups = c.user.repository_groups
194 _user_groups = c.user.user_groups
194 _user_groups = c.user.user_groups
195
195
196 handle_repos = None
196 handle_repos = None
197 handle_repo_groups = None
197 handle_repo_groups = None
198 handle_user_groups = None
198 handle_user_groups = None
199 # dummy call for flash of handle
199 # dummy call for flash of handle
200 set_handle_flash_repos = lambda: None
200 set_handle_flash_repos = lambda: None
201 set_handle_flash_repo_groups = lambda: None
201 set_handle_flash_repo_groups = lambda: None
202 set_handle_flash_user_groups = lambda: None
202 set_handle_flash_user_groups = lambda: None
203
203
204 if _repos and request.POST.get('user_repos'):
204 if _repos and request.POST.get('user_repos'):
205 do = request.POST['user_repos']
205 do = request.POST['user_repos']
206 if do == 'detach':
206 if do == 'detach':
207 handle_repos = 'detach'
207 handle_repos = 'detach'
208 set_handle_flash_repos = lambda: h.flash(
208 set_handle_flash_repos = lambda: h.flash(
209 _('Detached %s repositories') % len(_repos),
209 _('Detached %s repositories') % len(_repos),
210 category='success')
210 category='success')
211 elif do == 'delete':
211 elif do == 'delete':
212 handle_repos = 'delete'
212 handle_repos = 'delete'
213 set_handle_flash_repos = lambda: h.flash(
213 set_handle_flash_repos = lambda: h.flash(
214 _('Deleted %s repositories') % len(_repos),
214 _('Deleted %s repositories') % len(_repos),
215 category='success')
215 category='success')
216
216
217 if _repo_groups and request.POST.get('user_repo_groups'):
217 if _repo_groups and request.POST.get('user_repo_groups'):
218 do = request.POST['user_repo_groups']
218 do = request.POST['user_repo_groups']
219 if do == 'detach':
219 if do == 'detach':
220 handle_repo_groups = 'detach'
220 handle_repo_groups = 'detach'
221 set_handle_flash_repo_groups = lambda: h.flash(
221 set_handle_flash_repo_groups = lambda: h.flash(
222 _('Detached %s repository groups') % len(_repo_groups),
222 _('Detached %s repository groups') % len(_repo_groups),
223 category='success')
223 category='success')
224 elif do == 'delete':
224 elif do == 'delete':
225 handle_repo_groups = 'delete'
225 handle_repo_groups = 'delete'
226 set_handle_flash_repo_groups = lambda: h.flash(
226 set_handle_flash_repo_groups = lambda: h.flash(
227 _('Deleted %s repository groups') % len(_repo_groups),
227 _('Deleted %s repository groups') % len(_repo_groups),
228 category='success')
228 category='success')
229
229
230 if _user_groups and request.POST.get('user_user_groups'):
230 if _user_groups and request.POST.get('user_user_groups'):
231 do = request.POST['user_user_groups']
231 do = request.POST['user_user_groups']
232 if do == 'detach':
232 if do == 'detach':
233 handle_user_groups = 'detach'
233 handle_user_groups = 'detach'
234 set_handle_flash_user_groups = lambda: h.flash(
234 set_handle_flash_user_groups = lambda: h.flash(
235 _('Detached %s user groups') % len(_user_groups),
235 _('Detached %s user groups') % len(_user_groups),
236 category='success')
236 category='success')
237 elif do == 'delete':
237 elif do == 'delete':
238 handle_user_groups = 'delete'
238 handle_user_groups = 'delete'
239 set_handle_flash_user_groups = lambda: h.flash(
239 set_handle_flash_user_groups = lambda: h.flash(
240 _('Deleted %s user groups') % len(_user_groups),
240 _('Deleted %s user groups') % len(_user_groups),
241 category='success')
241 category='success')
242
242
243 old_values = c.user.get_api_data()
243 old_values = c.user.get_api_data()
244 try:
244 try:
245 UserModel().delete(c.user, handle_repos=handle_repos,
245 UserModel().delete(c.user, handle_repos=handle_repos,
246 handle_repo_groups=handle_repo_groups,
246 handle_repo_groups=handle_repo_groups,
247 handle_user_groups=handle_user_groups)
247 handle_user_groups=handle_user_groups)
248
248
249 audit_logger.store_web(
249 audit_logger.store_web(
250 'user.delete', action_data={'old_data': old_values},
250 'user.delete', action_data={'old_data': old_values},
251 user=c.rhodecode_user)
251 user=c.rhodecode_user)
252
252
253 Session().commit()
253 Session().commit()
254 set_handle_flash_repos()
254 set_handle_flash_repos()
255 set_handle_flash_repo_groups()
255 set_handle_flash_repo_groups()
256 set_handle_flash_user_groups()
256 set_handle_flash_user_groups()
257 h.flash(_('Successfully deleted user'), category='success')
257 h.flash(_('Successfully deleted user'), category='success')
258 except (UserOwnsReposException, UserOwnsRepoGroupsException,
258 except (UserOwnsReposException, UserOwnsRepoGroupsException,
259 UserOwnsUserGroupsException, DefaultUserException) as e:
259 UserOwnsUserGroupsException, DefaultUserException) as e:
260 h.flash(e, category='warning')
260 h.flash(e, category='warning')
261 except Exception:
261 except Exception:
262 log.exception("Exception during deletion of user")
262 log.exception("Exception during deletion of user")
263 h.flash(_('An error occurred during deletion of user'),
263 h.flash(_('An error occurred during deletion of user'),
264 category='error')
264 category='error')
265 return redirect(h.route_path('users'))
265 return redirect(h.route_path('users'))
266
266
267 @HasPermissionAllDecorator('hg.admin')
267 @HasPermissionAllDecorator('hg.admin')
268 @auth.CSRFRequired()
268 @auth.CSRFRequired()
269 def reset_password(self, user_id):
269 def reset_password(self, user_id):
270 """
270 """
271 toggle reset password flag for this user
271 toggle reset password flag for this user
272 """
272 """
273 user_id = safe_int(user_id)
273 user_id = safe_int(user_id)
274 c.user = User.get_or_404(user_id)
274 c.user = User.get_or_404(user_id)
275 try:
275 try:
276 old_value = c.user.user_data.get('force_password_change')
276 old_value = c.user.user_data.get('force_password_change')
277 c.user.update_userdata(force_password_change=not old_value)
277 c.user.update_userdata(force_password_change=not old_value)
278
278
279 if old_value:
279 if old_value:
280 msg = _('Force password change disabled for user')
280 msg = _('Force password change disabled for user')
281 audit_logger.store_web(
281 audit_logger.store_web(
282 'user.edit.password_reset.disabled',
282 'user.edit.password_reset.disabled',
283 user=c.rhodecode_user)
283 user=c.rhodecode_user)
284 else:
284 else:
285 msg = _('Force password change enabled for user')
285 msg = _('Force password change enabled for user')
286 audit_logger.store_web(
286 audit_logger.store_web(
287 'user.edit.password_reset.enabled',
287 'user.edit.password_reset.enabled',
288 user=c.rhodecode_user)
288 user=c.rhodecode_user)
289
289
290 Session().commit()
290 Session().commit()
291 h.flash(msg, category='success')
291 h.flash(msg, category='success')
292 except Exception:
292 except Exception:
293 log.exception("Exception during password reset for user")
293 log.exception("Exception during password reset for user")
294 h.flash(_('An error occurred during password reset for user'),
294 h.flash(_('An error occurred during password reset for user'),
295 category='error')
295 category='error')
296
296
297 return redirect(url('edit_user_advanced', user_id=user_id))
297 return redirect(url('edit_user_advanced', user_id=user_id))
298
298
299 @HasPermissionAllDecorator('hg.admin')
299 @HasPermissionAllDecorator('hg.admin')
300 @auth.CSRFRequired()
300 @auth.CSRFRequired()
301 def create_personal_repo_group(self, user_id):
301 def create_personal_repo_group(self, user_id):
302 """
302 """
303 Create personal repository group for this user
303 Create personal repository group for this user
304 """
304 """
305 from rhodecode.model.repo_group import RepoGroupModel
305 from rhodecode.model.repo_group import RepoGroupModel
306
306
307 user_id = safe_int(user_id)
307 user_id = safe_int(user_id)
308 c.user = User.get_or_404(user_id)
308 c.user = User.get_or_404(user_id)
309 personal_repo_group = RepoGroup.get_user_personal_repo_group(
309 personal_repo_group = RepoGroup.get_user_personal_repo_group(
310 c.user.user_id)
310 c.user.user_id)
311 if personal_repo_group:
311 if personal_repo_group:
312 return redirect(url('edit_user_advanced', user_id=user_id))
312 return redirect(url('edit_user_advanced', user_id=user_id))
313
313
314 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
314 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
315 c.user)
315 c.user)
316 named_personal_group = RepoGroup.get_by_group_name(
316 named_personal_group = RepoGroup.get_by_group_name(
317 personal_repo_group_name)
317 personal_repo_group_name)
318 try:
318 try:
319
319
320 if named_personal_group and named_personal_group.user_id == c.user.user_id:
320 if named_personal_group and named_personal_group.user_id == c.user.user_id:
321 # migrate the same named group, and mark it as personal
321 # migrate the same named group, and mark it as personal
322 named_personal_group.personal = True
322 named_personal_group.personal = True
323 Session().add(named_personal_group)
323 Session().add(named_personal_group)
324 Session().commit()
324 Session().commit()
325 msg = _('Linked repository group `%s` as personal' % (
325 msg = _('Linked repository group `%s` as personal' % (
326 personal_repo_group_name,))
326 personal_repo_group_name,))
327 h.flash(msg, category='success')
327 h.flash(msg, category='success')
328 elif not named_personal_group:
328 elif not named_personal_group:
329 RepoGroupModel().create_personal_repo_group(c.user)
329 RepoGroupModel().create_personal_repo_group(c.user)
330
330
331 msg = _('Created repository group `%s`' % (
331 msg = _('Created repository group `%s`' % (
332 personal_repo_group_name,))
332 personal_repo_group_name,))
333 h.flash(msg, category='success')
333 h.flash(msg, category='success')
334 else:
334 else:
335 msg = _('Repository group `%s` is already taken' % (
335 msg = _('Repository group `%s` is already taken' % (
336 personal_repo_group_name,))
336 personal_repo_group_name,))
337 h.flash(msg, category='warning')
337 h.flash(msg, category='warning')
338 except Exception:
338 except Exception:
339 log.exception("Exception during repository group creation")
339 log.exception("Exception during repository group creation")
340 msg = _(
340 msg = _(
341 'An error occurred during repository group creation for user')
341 'An error occurred during repository group creation for user')
342 h.flash(msg, category='error')
342 h.flash(msg, category='error')
343 Session().rollback()
343 Session().rollback()
344
344
345 return redirect(url('edit_user_advanced', user_id=user_id))
345 return redirect(url('edit_user_advanced', user_id=user_id))
346
346
347 @HasPermissionAllDecorator('hg.admin')
347 @HasPermissionAllDecorator('hg.admin')
348 def show(self, user_id):
348 def show(self, user_id):
349 """GET /users/user_id: Show a specific item"""
349 """GET /users/user_id: Show a specific item"""
350 # url('user', user_id=ID)
350 # url('user', user_id=ID)
351 User.get_or_404(-1)
351 User.get_or_404(-1)
352
352
353 @HasPermissionAllDecorator('hg.admin')
353 @HasPermissionAllDecorator('hg.admin')
354 def edit(self, user_id):
354 def edit(self, user_id):
355 """GET /users/user_id/edit: Form to edit an existing item"""
355 """GET /users/user_id/edit: Form to edit an existing item"""
356 # url('edit_user', user_id=ID)
356 # url('edit_user', user_id=ID)
357 user_id = safe_int(user_id)
357 user_id = safe_int(user_id)
358 c.user = User.get_or_404(user_id)
358 c.user = User.get_or_404(user_id)
359 if c.user.username == User.DEFAULT_USER:
359 if c.user.username == User.DEFAULT_USER:
360 h.flash(_("You can't edit this user"), category='warning')
360 h.flash(_("You can't edit this user"), category='warning')
361 return redirect(h.route_path('users'))
361 return redirect(h.route_path('users'))
362
362
363 c.active = 'profile'
363 c.active = 'profile'
364 c.extern_type = c.user.extern_type
364 c.extern_type = c.user.extern_type
365 c.extern_name = c.user.extern_name
365 c.extern_name = c.user.extern_name
366 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
366 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
367
367
368 defaults = c.user.get_dict()
368 defaults = c.user.get_dict()
369 defaults.update({'language': c.user.user_data.get('language')})
369 defaults.update({'language': c.user.user_data.get('language')})
370 return htmlfill.render(
370 return htmlfill.render(
371 render('admin/users/user_edit.mako'),
371 render('admin/users/user_edit.mako'),
372 defaults=defaults,
372 defaults=defaults,
373 encoding="UTF-8",
373 encoding="UTF-8",
374 force_defaults=False)
374 force_defaults=False)
375
375
376 @HasPermissionAllDecorator('hg.admin')
376 @HasPermissionAllDecorator('hg.admin')
377 def edit_advanced(self, user_id):
377 def edit_advanced(self, user_id):
378 user_id = safe_int(user_id)
378 user_id = safe_int(user_id)
379 user = c.user = User.get_or_404(user_id)
379 user = c.user = User.get_or_404(user_id)
380 if user.username == User.DEFAULT_USER:
380 if user.username == User.DEFAULT_USER:
381 h.flash(_("You can't edit this user"), category='warning')
381 h.flash(_("You can't edit this user"), category='warning')
382 return redirect(h.route_path('users'))
382 return redirect(h.route_path('users'))
383
383
384 c.active = 'advanced'
384 c.active = 'advanced'
385 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
385 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
386 c.personal_repo_group_name = RepoGroupModel()\
386 c.personal_repo_group_name = RepoGroupModel()\
387 .get_personal_group_name(user)
387 .get_personal_group_name(user)
388 c.first_admin = User.get_first_super_admin()
388 c.first_admin = User.get_first_super_admin()
389 defaults = user.get_dict()
389 defaults = user.get_dict()
390
390
391 # Interim workaround if the user participated on any pull requests as a
391 # Interim workaround if the user participated on any pull requests as a
392 # reviewer.
392 # reviewer.
393 has_review = bool(PullRequestReviewers.query().filter(
393 has_review = bool(PullRequestReviewers.query().filter(
394 PullRequestReviewers.user_id == user_id).first())
394 PullRequestReviewers.user_id == user_id).first())
395 c.can_delete_user = not has_review
395 c.can_delete_user = not has_review
396 c.can_delete_user_message = _(
396 c.can_delete_user_message = _(
397 'The user participates as reviewer in pull requests and '
397 'The user participates as reviewer in pull requests and '
398 'cannot be deleted. You can set the user to '
398 'cannot be deleted. You can set the user to '
399 '"inactive" instead of deleting it.') if has_review else ''
399 '"inactive" instead of deleting it.') if has_review else ''
400
400
401 return htmlfill.render(
401 return htmlfill.render(
402 render('admin/users/user_edit.mako'),
402 render('admin/users/user_edit.mako'),
403 defaults=defaults,
403 defaults=defaults,
404 encoding="UTF-8",
404 encoding="UTF-8",
405 force_defaults=False)
405 force_defaults=False)
406
406
407 @HasPermissionAllDecorator('hg.admin')
407 @HasPermissionAllDecorator('hg.admin')
408 def edit_global_perms(self, user_id):
408 def edit_global_perms(self, user_id):
409 user_id = safe_int(user_id)
409 user_id = safe_int(user_id)
410 c.user = User.get_or_404(user_id)
410 c.user = User.get_or_404(user_id)
411 if c.user.username == User.DEFAULT_USER:
411 if c.user.username == User.DEFAULT_USER:
412 h.flash(_("You can't edit this user"), category='warning')
412 h.flash(_("You can't edit this user"), category='warning')
413 return redirect(h.route_path('users'))
413 return redirect(h.route_path('users'))
414
414
415 c.active = 'global_perms'
415 c.active = 'global_perms'
416
416
417 c.default_user = User.get_default_user()
417 c.default_user = User.get_default_user()
418 defaults = c.user.get_dict()
418 defaults = c.user.get_dict()
419 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
419 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
420 defaults.update(c.default_user.get_default_perms())
420 defaults.update(c.default_user.get_default_perms())
421 defaults.update(c.user.get_default_perms())
421 defaults.update(c.user.get_default_perms())
422
422
423 return htmlfill.render(
423 return htmlfill.render(
424 render('admin/users/user_edit.mako'),
424 render('admin/users/user_edit.mako'),
425 defaults=defaults,
425 defaults=defaults,
426 encoding="UTF-8",
426 encoding="UTF-8",
427 force_defaults=False)
427 force_defaults=False)
428
428
429 @HasPermissionAllDecorator('hg.admin')
429 @HasPermissionAllDecorator('hg.admin')
430 @auth.CSRFRequired()
430 @auth.CSRFRequired()
431 def update_global_perms(self, user_id):
431 def update_global_perms(self, user_id):
432 user_id = safe_int(user_id)
432 user_id = safe_int(user_id)
433 user = User.get_or_404(user_id)
433 user = User.get_or_404(user_id)
434 c.active = 'global_perms'
434 c.active = 'global_perms'
435 try:
435 try:
436 # first stage that verifies the checkbox
436 # first stage that verifies the checkbox
437 _form = UserIndividualPermissionsForm()
437 _form = UserIndividualPermissionsForm()
438 form_result = _form.to_python(dict(request.POST))
438 form_result = _form.to_python(dict(request.POST))
439 inherit_perms = form_result['inherit_default_permissions']
439 inherit_perms = form_result['inherit_default_permissions']
440 user.inherit_default_permissions = inherit_perms
440 user.inherit_default_permissions = inherit_perms
441 Session().add(user)
441 Session().add(user)
442
442
443 if not inherit_perms:
443 if not inherit_perms:
444 # only update the individual ones if we un check the flag
444 # only update the individual ones if we un check the flag
445 _form = UserPermissionsForm(
445 _form = UserPermissionsForm(
446 [x[0] for x in c.repo_create_choices],
446 [x[0] for x in c.repo_create_choices],
447 [x[0] for x in c.repo_create_on_write_choices],
447 [x[0] for x in c.repo_create_on_write_choices],
448 [x[0] for x in c.repo_group_create_choices],
448 [x[0] for x in c.repo_group_create_choices],
449 [x[0] for x in c.user_group_create_choices],
449 [x[0] for x in c.user_group_create_choices],
450 [x[0] for x in c.fork_choices],
450 [x[0] for x in c.fork_choices],
451 [x[0] for x in c.inherit_default_permission_choices])()
451 [x[0] for x in c.inherit_default_permission_choices])()
452
452
453 form_result = _form.to_python(dict(request.POST))
453 form_result = _form.to_python(dict(request.POST))
454 form_result.update({'perm_user_id': user.user_id})
454 form_result.update({'perm_user_id': user.user_id})
455
455
456 PermissionModel().update_user_permissions(form_result)
456 PermissionModel().update_user_permissions(form_result)
457
457
458 # TODO(marcink): implement global permissions
458 # TODO(marcink): implement global permissions
459 # audit_log.store_web('user.edit.permissions')
459 # audit_log.store_web('user.edit.permissions')
460
460
461 Session().commit()
461 Session().commit()
462 h.flash(_('User global permissions updated successfully'),
462 h.flash(_('User global permissions updated successfully'),
463 category='success')
463 category='success')
464
464
465 except formencode.Invalid as errors:
465 except formencode.Invalid as errors:
466 defaults = errors.value
466 defaults = errors.value
467 c.user = user
467 c.user = user
468 return htmlfill.render(
468 return htmlfill.render(
469 render('admin/users/user_edit.mako'),
469 render('admin/users/user_edit.mako'),
470 defaults=defaults,
470 defaults=defaults,
471 errors=errors.error_dict or {},
471 errors=errors.error_dict or {},
472 prefix_error=False,
472 prefix_error=False,
473 encoding="UTF-8",
473 encoding="UTF-8",
474 force_defaults=False)
474 force_defaults=False)
475 except Exception:
475 except Exception:
476 log.exception("Exception during permissions saving")
476 log.exception("Exception during permissions saving")
477 h.flash(_('An error occurred during permissions saving'),
477 h.flash(_('An error occurred during permissions saving'),
478 category='error')
478 category='error')
479 return redirect(url('edit_user_global_perms', user_id=user_id))
479 return redirect(url('edit_user_global_perms', user_id=user_id))
480
480
481 @HasPermissionAllDecorator('hg.admin')
481 @HasPermissionAllDecorator('hg.admin')
482 def edit_perms_summary(self, user_id):
482 def edit_perms_summary(self, user_id):
483 user_id = safe_int(user_id)
483 user_id = safe_int(user_id)
484 c.user = User.get_or_404(user_id)
484 c.user = User.get_or_404(user_id)
485 if c.user.username == User.DEFAULT_USER:
485 if c.user.username == User.DEFAULT_USER:
486 h.flash(_("You can't edit this user"), category='warning')
486 h.flash(_("You can't edit this user"), category='warning')
487 return redirect(h.route_path('users'))
487 return redirect(h.route_path('users'))
488
488
489 c.active = 'perms_summary'
489 c.active = 'perms_summary'
490 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
490 c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr)
491
491
492 return render('admin/users/user_edit.mako')
492 return render('admin/users/user_edit.mako')
493
493
494 @HasPermissionAllDecorator('hg.admin')
495 def edit_emails(self, user_id):
496 user_id = safe_int(user_id)
497 c.user = User.get_or_404(user_id)
498 if c.user.username == User.DEFAULT_USER:
499 h.flash(_("You can't edit this user"), category='warning')
500 return redirect(h.route_path('users'))
501
502 c.active = 'emails'
503 c.user_email_map = UserEmailMap.query() \
504 .filter(UserEmailMap.user == c.user).all()
505
506 defaults = c.user.get_dict()
507 return htmlfill.render(
508 render('admin/users/user_edit.mako'),
509 defaults=defaults,
510 encoding="UTF-8",
511 force_defaults=False)
512
513 @HasPermissionAllDecorator('hg.admin')
514 @auth.CSRFRequired()
515 def add_email(self, user_id):
516 user_id = safe_int(user_id)
517 c.user = User.get_or_404(user_id)
518
519 email = request.POST.get('new_email')
520 user_model = UserModel()
521 user_data = c.user.get_api_data()
522 try:
523 user_model.add_extra_email(user_id, email)
524 audit_logger.store_web(
525 'user.edit.email.add',
526 action_data={'email': email, 'user': user_data},
527 user=c.rhodecode_user)
528 Session().commit()
529 h.flash(_("Added new email address `%s` for user account") % email,
530 category='success')
531 except formencode.Invalid as error:
532 msg = error.error_dict['email']
533 h.flash(msg, category='error')
534 except Exception:
535 log.exception("Exception during email saving")
536 h.flash(_('An error occurred during email saving'),
537 category='error')
538 return redirect(url('edit_user_emails', user_id=user_id))
539
540 @HasPermissionAllDecorator('hg.admin')
541 @auth.CSRFRequired()
542 def delete_email(self, user_id):
543 user_id = safe_int(user_id)
544 c.user = User.get_or_404(user_id)
545 email_id = request.POST.get('del_email_id')
546 user_model = UserModel()
547
548 email = UserEmailMap.query().get(email_id).email
549 user_data = c.user.get_api_data()
550 user_model.delete_extra_email(user_id, email_id)
551 audit_logger.store_web(
552 'user.edit.email.delete',
553 action_data={'email': email, 'user': user_data},
554 user=c.rhodecode_user)
555 Session().commit()
556 h.flash(_("Removed email address from user account"), category='success')
557 return redirect(url('edit_user_emails', user_id=user_id))
558
559 @HasPermissionAllDecorator('hg.admin')
560 def edit_ips(self, user_id):
561 user_id = safe_int(user_id)
562 c.user = User.get_or_404(user_id)
563 if c.user.username == User.DEFAULT_USER:
564 h.flash(_("You can't edit this user"), category='warning')
565 return redirect(h.route_path('users'))
566
567 c.active = 'ips'
568 c.user_ip_map = UserIpMap.query() \
569 .filter(UserIpMap.user == c.user).all()
570
571 c.inherit_default_ips = c.user.inherit_default_permissions
572 c.default_user_ip_map = UserIpMap.query() \
573 .filter(UserIpMap.user == User.get_default_user()).all()
574
575 defaults = c.user.get_dict()
576 return htmlfill.render(
577 render('admin/users/user_edit.mako'),
578 defaults=defaults,
579 encoding="UTF-8",
580 force_defaults=False)
581
582 @HasPermissionAllDecorator('hg.admin')
583 @auth.CSRFRequired()
584 def add_ip(self, user_id):
585 user_id = safe_int(user_id)
586 c.user = User.get_or_404(user_id)
587 user_model = UserModel()
588 try:
589 ip_list = user_model.parse_ip_range(request.POST.get('new_ip'))
590 except Exception as e:
591 ip_list = []
592 log.exception("Exception during ip saving")
593 h.flash(_('An error occurred during ip saving:%s' % (e,)),
594 category='error')
595
596 desc = request.POST.get('description')
597 added = []
598 user_data = c.user.get_api_data()
599 for ip in ip_list:
600 try:
601 user_model.add_extra_ip(user_id, ip, desc)
602 audit_logger.store_web(
603 'user.edit.ip.add',
604 action_data={'ip': ip, 'user': user_data},
605 user=c.rhodecode_user)
606 Session().commit()
607 added.append(ip)
608 except formencode.Invalid as error:
609 msg = error.error_dict['ip']
610 h.flash(msg, category='error')
611 except Exception:
612 log.exception("Exception during ip saving")
613 h.flash(_('An error occurred during ip saving'),
614 category='error')
615 if added:
616 h.flash(
617 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
618 category='success')
619 if 'default_user' in request.POST:
620 return redirect(url('admin_permissions_ips'))
621 return redirect(url('edit_user_ips', user_id=user_id))
622
623 @HasPermissionAllDecorator('hg.admin')
624 @auth.CSRFRequired()
625 def delete_ip(self, user_id):
626 user_id = safe_int(user_id)
627 c.user = User.get_or_404(user_id)
628
629 ip_id = request.POST.get('del_ip_id')
630 user_model = UserModel()
631 user_data = c.user.get_api_data()
632 ip = UserIpMap.query().get(ip_id).ip_addr
633 user_model.delete_extra_ip(user_id, ip_id)
634 audit_logger.store_web(
635 'user.edit.ip.delete',
636 action_data={'ip': ip, 'user': user_data},
637 user=c.rhodecode_user)
638 Session().commit()
639 h.flash(_("Removed ip address from user whitelist"), category='success')
640
641 if 'default_user' in request.POST:
642 return redirect(url('admin_permissions_ips'))
643 return redirect(url('edit_user_ips', user_id=user_id))
@@ -1,147 +1,154 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('gists', '/_admin/gists', []);
18 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
19 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
20 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
21 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
22 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
22 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
23 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
23 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
24 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
24 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
25 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
25 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
26 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
26 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
27 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
27 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
28 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
28 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
29 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
29 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
30 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
30 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
31 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
31 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
32 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
32 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
33 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
33 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
34 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
34 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
35 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
35 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
36 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
36 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
37 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
37 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
38 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
38 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
39 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
40 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
41 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
43 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
43 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
44 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
45 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
46 pyroutes.register('favicon', '/favicon.ico', []);
46 pyroutes.register('favicon', '/favicon.ico', []);
47 pyroutes.register('robots', '/robots.txt', []);
47 pyroutes.register('robots', '/robots.txt', []);
48 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
48 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
49 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
49 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
50 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
50 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
51 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
51 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
52 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
52 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
53 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
53 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
54 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
54 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
55 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
55 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
56 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
56 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
57 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
57 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
58 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
58 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
59 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
59 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
60 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
60 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
61 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
61 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
62 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
62 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
63 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
63 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
64 pyroutes.register('ops_ping', '_admin/ops/ping', []);
64 pyroutes.register('ops_ping', '_admin/ops/ping', []);
65 pyroutes.register('admin_home', '/_admin', []);
65 pyroutes.register('admin_home', '/_admin', []);
66 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
66 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
67 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
67 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
68 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
68 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
69 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
69 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
70 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
70 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
71 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
71 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
72 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
72 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
73 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
73 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
74 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
74 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
75 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
76 pyroutes.register('admin_permissions_ips', '_admin/permissions/ips', []);
76 pyroutes.register('users', '_admin/users', []);
77 pyroutes.register('users', '_admin/users', []);
77 pyroutes.register('users_data', '_admin/users_data', []);
78 pyroutes.register('users_data', '_admin/users_data', []);
78 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
81 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
82 pyroutes.register('edit_user_emails', '_admin/users/%(user_id)s/edit/emails', ['user_id']);
83 pyroutes.register('edit_user_emails_add', '_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
84 pyroutes.register('edit_user_emails_delete', '_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
85 pyroutes.register('edit_user_ips', '_admin/users/%(user_id)s/edit/ips', ['user_id']);
86 pyroutes.register('edit_user_ips_add', '_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
87 pyroutes.register('edit_user_ips_delete', '_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
81 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
88 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
82 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
89 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
83 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
90 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
84 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
91 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
85 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
92 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
86 pyroutes.register('channelstream_proxy', '/_channelstream', []);
93 pyroutes.register('channelstream_proxy', '/_channelstream', []);
87 pyroutes.register('login', '/_admin/login', []);
94 pyroutes.register('login', '/_admin/login', []);
88 pyroutes.register('logout', '/_admin/logout', []);
95 pyroutes.register('logout', '/_admin/logout', []);
89 pyroutes.register('register', '/_admin/register', []);
96 pyroutes.register('register', '/_admin/register', []);
90 pyroutes.register('reset_password', '/_admin/password_reset', []);
97 pyroutes.register('reset_password', '/_admin/password_reset', []);
91 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
98 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
92 pyroutes.register('home', '/', []);
99 pyroutes.register('home', '/', []);
93 pyroutes.register('user_autocomplete_data', '/_users', []);
100 pyroutes.register('user_autocomplete_data', '/_users', []);
94 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
101 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
95 pyroutes.register('repo_list_data', '/_repos', []);
102 pyroutes.register('repo_list_data', '/_repos', []);
96 pyroutes.register('goto_switcher_data', '/_goto_data', []);
103 pyroutes.register('goto_switcher_data', '/_goto_data', []);
97 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
104 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
98 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
105 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
99 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
106 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
100 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
107 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
101 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
108 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
102 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
109 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
103 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
110 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
104 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
111 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
105 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
112 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
106 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
113 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
107 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
114 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
108 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
115 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
116 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
117 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
118 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
112 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
119 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
113 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
120 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
114 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
121 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
115 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
122 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
116 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
123 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
117 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
124 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
118 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
125 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
119 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
126 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
120 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
127 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
121 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
128 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
122 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
129 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
123 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
130 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
124 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
131 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
125 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
132 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
126 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
133 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
127 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
134 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
128 pyroutes.register('search', '/_admin/search', []);
135 pyroutes.register('search', '/_admin/search', []);
129 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
136 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
130 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
137 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
131 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
138 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
132 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
139 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
133 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
140 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
134 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
141 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
135 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
142 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
136 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
143 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
137 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
144 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
138 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
145 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
139 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
146 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
140 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
147 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
141 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
148 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
142 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
149 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
143 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
150 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
144 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
151 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
145 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
152 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
146 pyroutes.register('apiv2', '/_admin/api', []);
153 pyroutes.register('apiv2', '/_admin/api', []);
147 }
154 }
@@ -1,67 +1,67 b''
1
1
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Default IP Whitelist For All Users')}</h3>
5 <h3 class="panel-title">${_('Default IP Whitelist For All Users')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="ips_wrap">
8 <div class="ips_wrap">
9 <table class="rctable ip-whitelist">
9 <table class="rctable ip-whitelist">
10 <tr>
10 <tr>
11 <th>IP Address</th>
11 <th>IP Address</th>
12 <th>IP Range</th>
12 <th>IP Range</th>
13 <th>Description</th>
13 <th>Description</th>
14 <th></th>
14 <th></th>
15 </tr>
15 </tr>
16 %if c.user_ip_map:
16 %if c.user_ip_map:
17 %for ip in c.user_ip_map:
17 %for ip in c.user_ip_map:
18 <tr>
18 <tr>
19 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
19 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
20 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
20 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
21 <td class="td-description"><div class="ip">${ip.description}</div></td>
21 <td class="td-description"><div class="ip">${ip.description}</div></td>
22 <td class="td-action">
22 <td class="td-action">
23 ${h.secure_form(url('edit_user_ips', user_id=c.user.user_id),method='delete')}
23 ${h.secure_form(h.route_path('edit_user_ips_delete', user_id=c.user.user_id), method='POST')}
24 ${h.hidden('del_ip_id',ip.ip_id)}
24 ${h.hidden('del_ip_id',ip.ip_id)}
25 ${h.hidden('default_user', 'True')}
25 ${h.hidden('default_user', 'True')}
26 ${h.submit('remove_',_('Delete'),id="remove_ip_%s" % ip.ip_id,
26 ${h.submit('remove_',_('Delete'),id="remove_ip_%s" % ip.ip_id,
27 class_="btn btn-link btn-danger", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
27 class_="btn btn-link btn-danger", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
28 ${h.end_form()}
28 ${h.end_form()}
29 </td>
29 </td>
30 </tr>
30 </tr>
31 %endfor
31 %endfor
32 %else:
32 %else:
33 <tr>
33 <tr>
34 <td class="ip">${_('All IP addresses are allowed')}</td>
34 <td class="ip">${_('All IP addresses are allowed')}</td>
35 <td></td>
35 <td></td>
36 <td></td>
36 <td></td>
37 <td></td>
37 <td></td>
38 </tr>
38 </tr>
39 %endif
39 %endif
40 </table>
40 </table>
41 </div>
41 </div>
42
42
43 ${h.secure_form(url('edit_user_ips', user_id=c.user.user_id),method='put')}
43 ${h.secure_form(h.route_path('edit_user_ips_add', user_id=c.user.user_id), method='POST')}
44 <div class="form">
44 <div class="form">
45 <!-- fields -->
45 <!-- fields -->
46 <div class="fields">
46 <div class="fields">
47 <div class="field">
47 <div class="field">
48 <div class="label">
48 <div class="label">
49 <label for="new_ip">${_('New IP Address')}:</label>
49 <label for="new_ip">${_('New IP Address')}:</label>
50 </div>
50 </div>
51 <div class="input">
51 <div class="input">
52 ${h.hidden('default_user', 'True')}
52 ${h.hidden('default_user', 'True')}
53 ${h.text('new_ip')} ${h.text('description', placeholder=_('Description...'))}
53 ${h.text('new_ip')} ${h.text('description', placeholder=_('Description...'))}
54 <span class="help-block">${_('Enter a comma separated list of IP Addresses like 127.0.0.1,\n'
54 <span class="help-block">${_('Enter a comma separated list of IP Addresses like 127.0.0.1,\n'
55 'or use an IP Address with a mask 127.0.0.1/24, to create a network range.\n'
55 'or use an IP Address with a mask 127.0.0.1/24, to create a network range.\n'
56 'To specify multiple addresses in a range, use the 127.0.0.1-127.0.0.10 syntax')}</span>
56 'To specify multiple addresses in a range, use the 127.0.0.1-127.0.0.10 syntax')}</span>
57 </div>
57 </div>
58 </div>
58 </div>
59 <div class="buttons">
59 <div class="buttons">
60 ${h.submit('save',_('Add'),class_="btn")}
60 ${h.submit('save',_('Add'),class_="btn")}
61 ${h.reset('reset',_('Reset'),class_="btn")}
61 ${h.reset('reset',_('Reset'),class_="btn")}
62 </div>
62 </div>
63 </div>
63 </div>
64 </div>
64 </div>
65 ${h.end_form()}
65 ${h.end_form()}
66 </div>
66 </div>
67 </div>
67 </div>
@@ -1,56 +1,56 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user settings') % c.user.username}
5 ${_('%s user settings') % c.user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Users'),h.route_path('users'))}
14 ${h.link_to(_('Users'),h.route_path('users'))}
15 &raquo;
15 &raquo;
16 % if c.user.active:
16 % if c.user.active:
17 ${c.user.username}
17 ${c.user.username}
18 % else:
18 % else:
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
20 % endif
20 % endif
21
21
22 </%def>
22 </%def>
23
23
24 <%def name="menu_bar_nav()">
24 <%def name="menu_bar_nav()">
25 ${self.menu_items(active='admin')}
25 ${self.menu_items(active='admin')}
26 </%def>
26 </%def>
27
27
28 <%def name="main()">
28 <%def name="main()">
29 <div class="box user_settings">
29 <div class="box user_settings">
30 <div class="title">
30 <div class="title">
31 ${self.breadcrumbs()}
31 ${self.breadcrumbs()}
32 </div>
32 </div>
33
33
34 ##main
34 ##main
35 <div class="sidebar-col-wrapper">
35 <div class="sidebar-col-wrapper">
36 <div class="sidebar">
36 <div class="sidebar">
37 <ul class="nav nav-pills nav-stacked">
37 <ul class="nav nav-pills nav-stacked">
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.url('edit_user', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
40 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
40 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_user_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
41 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
41 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.url('edit_user_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
42 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
42 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.url('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
43 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.url('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
43 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
44 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.url('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
44 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
45 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
45 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
46 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
46 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
47 </ul>
47 </ul>
48 </div>
48 </div>
49
49
50 <div class="main-content-full-width">
50 <div class="main-content-full-width">
51 <%include file="/admin/users/user_edit_${c.active}.mako"/>
51 <%include file="/admin/users/user_edit_${c.active}.mako"/>
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55
55
56 </%def>
56 </%def>
@@ -1,157 +1,157 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="apikeys_wrap">
6 <div class="apikeys_wrap">
7 <p>
7 <p>
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
10 </p>
10 </p>
11 <table class="rctable auth_tokens">
11 <table class="rctable auth_tokens">
12 <tr>
12 <tr>
13 <th>${_('Token')}</th>
13 <th>${_('Token')}</th>
14 <th>${_('Scope')}</th>
14 <th>${_('Scope')}</th>
15 <th>${_('Description')}</th>
15 <th>${_('Description')}</th>
16 <th>${_('Role')}</th>
16 <th>${_('Role')}</th>
17 <th>${_('Expiration')}</th>
17 <th>${_('Expiration')}</th>
18 <th>${_('Action')}</th>
18 <th>${_('Action')}</th>
19 </tr>
19 </tr>
20 %if c.user_auth_tokens:
20 %if c.user_auth_tokens:
21 %for auth_token in c.user_auth_tokens:
21 %for auth_token in c.user_auth_tokens:
22 <tr class="${'expired' if auth_token.expired else ''}">
22 <tr class="${'expired' if auth_token.expired else ''}">
23 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
23 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
24 <td class="td">${auth_token.scope_humanized}</td>
24 <td class="td">${auth_token.scope_humanized}</td>
25 <td class="td-wrap">${auth_token.description}</td>
25 <td class="td-wrap">${auth_token.description}</td>
26 <td class="td-tags">
26 <td class="td-tags">
27 <span class="tag disabled">${auth_token.role_humanized}</span>
27 <span class="tag disabled">${auth_token.role_humanized}</span>
28 </td>
28 </td>
29 <td class="td-exp">
29 <td class="td-exp">
30 %if auth_token.expires == -1:
30 %if auth_token.expires == -1:
31 ${_('never')}
31 ${_('never')}
32 %else:
32 %else:
33 %if auth_token.expired:
33 %if auth_token.expired:
34 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
34 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
35 %else:
35 %else:
36 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
36 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
37 %endif
37 %endif
38 %endif
38 %endif
39 </td>
39 </td>
40 <td class="td-action">
40 <td class="td-action">
41 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), method='post')}
41 ${h.secure_form(h.route_path('edit_user_auth_tokens_delete', user_id=c.user.user_id), method='POST')}
42 ${h.hidden('del_auth_token',auth_token.api_key)}
42 ${h.hidden('del_auth_token', auth_token.user_api_key_id)}
43 <button class="btn btn-link btn-danger" type="submit"
43 <button class="btn btn-link btn-danger" type="submit"
44 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
44 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.token_obfuscated}');">
45 ${_('Delete')}
45 ${_('Delete')}
46 </button>
46 </button>
47 ${h.end_form()}
47 ${h.end_form()}
48 </td>
48 </td>
49 </tr>
49 </tr>
50 %endfor
50 %endfor
51 %else:
51 %else:
52 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
52 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
53 %endif
53 %endif
54 </table>
54 </table>
55 </div>
55 </div>
56
56
57 <div class="user_auth_tokens">
57 <div class="user_auth_tokens">
58 ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), method='post')}
58 ${h.secure_form(h.route_path('edit_user_auth_tokens_add', user_id=c.user.user_id), method='POST')}
59 <div class="form form-vertical">
59 <div class="form form-vertical">
60 <!-- fields -->
60 <!-- fields -->
61 <div class="fields">
61 <div class="fields">
62 <div class="field">
62 <div class="field">
63 <div class="label">
63 <div class="label">
64 <label for="new_email">${_('New authentication token')}:</label>
64 <label for="new_email">${_('New authentication token')}:</label>
65 </div>
65 </div>
66 <div class="input">
66 <div class="input">
67 ${h.text('description', class_='medium', placeholder=_('Description'))}
67 ${h.text('description', class_='medium', placeholder=_('Description'))}
68 ${h.select('lifetime', '', c.lifetime_options)}
68 ${h.select('lifetime', '', c.lifetime_options)}
69 ${h.select('role', '', c.role_options)}
69 ${h.select('role', '', c.role_options)}
70
70
71 % if c.allow_scoped_tokens:
71 % if c.allow_scoped_tokens:
72 ${h.hidden('scope_repo_id')}
72 ${h.hidden('scope_repo_id')}
73 % else:
73 % else:
74 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
74 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
75 % endif
75 % endif
76 </div>
76 </div>
77 <p class="help-block">
77 <p class="help-block">
78 ${_('Repository scope works only with tokens with VCS type.')}
78 ${_('Repository scope works only with tokens with VCS type.')}
79 </p>
79 </p>
80 </div>
80 </div>
81 <div class="buttons">
81 <div class="buttons">
82 ${h.submit('save',_('Add'),class_="btn")}
82 ${h.submit('save',_('Add'),class_="btn")}
83 ${h.reset('reset',_('Reset'),class_="btn")}
83 ${h.reset('reset',_('Reset'),class_="btn")}
84 </div>
84 </div>
85 </div>
85 </div>
86 </div>
86 </div>
87 ${h.end_form()}
87 ${h.end_form()}
88 </div>
88 </div>
89 </div>
89 </div>
90 </div>
90 </div>
91
91
92 <script>
92 <script>
93
93
94 $(document).ready(function(){
94 $(document).ready(function(){
95 var select2Options = {
95 var select2Options = {
96 'containerCssClass': "drop-menu",
96 'containerCssClass': "drop-menu",
97 'dropdownCssClass': "drop-menu-dropdown",
97 'dropdownCssClass': "drop-menu-dropdown",
98 'dropdownAutoWidth': true
98 'dropdownAutoWidth': true
99 };
99 };
100 $("#lifetime").select2(select2Options);
100 $("#lifetime").select2(select2Options);
101 $("#role").select2(select2Options);
101 $("#role").select2(select2Options);
102
102
103 var repoFilter = function(data) {
103 var repoFilter = function(data) {
104 var results = [];
104 var results = [];
105
105
106 if (!data.results[0]) {
106 if (!data.results[0]) {
107 return data
107 return data
108 }
108 }
109
109
110 $.each(data.results[0].children, function() {
110 $.each(data.results[0].children, function() {
111 // replace name to ID for submision
111 // replace name to ID for submision
112 this.id = this.obj.repo_id;
112 this.id = this.obj.repo_id;
113 results.push(this);
113 results.push(this);
114 });
114 });
115
115
116 data.results[0].children = results;
116 data.results[0].children = results;
117 return data;
117 return data;
118 };
118 };
119
119
120 $("#scope_repo_id_disabled").select2(select2Options);
120 $("#scope_repo_id_disabled").select2(select2Options);
121
121
122 $("#scope_repo_id").select2({
122 $("#scope_repo_id").select2({
123 cachedDataSource: {},
123 cachedDataSource: {},
124 minimumInputLength: 2,
124 minimumInputLength: 2,
125 placeholder: "${_('repository scope')}",
125 placeholder: "${_('repository scope')}",
126 dropdownAutoWidth: true,
126 dropdownAutoWidth: true,
127 containerCssClass: "drop-menu",
127 containerCssClass: "drop-menu",
128 dropdownCssClass: "drop-menu-dropdown",
128 dropdownCssClass: "drop-menu-dropdown",
129 formatResult: formatResult,
129 formatResult: formatResult,
130 query: $.debounce(250, function(query){
130 query: $.debounce(250, function(query){
131 self = this;
131 self = this;
132 var cacheKey = query.term;
132 var cacheKey = query.term;
133 var cachedData = self.cachedDataSource[cacheKey];
133 var cachedData = self.cachedDataSource[cacheKey];
134
134
135 if (cachedData) {
135 if (cachedData) {
136 query.callback({results: cachedData.results});
136 query.callback({results: cachedData.results});
137 } else {
137 } else {
138 $.ajax({
138 $.ajax({
139 url: pyroutes.url('repo_list_data'),
139 url: pyroutes.url('repo_list_data'),
140 data: {'query': query.term},
140 data: {'query': query.term},
141 dataType: 'json',
141 dataType: 'json',
142 type: 'GET',
142 type: 'GET',
143 success: function(data) {
143 success: function(data) {
144 data = repoFilter(data);
144 data = repoFilter(data);
145 self.cachedDataSource[cacheKey] = data;
145 self.cachedDataSource[cacheKey] = data;
146 query.callback({results: data.results});
146 query.callback({results: data.results});
147 },
147 },
148 error: function(data, textStatus, errorThrown) {
148 error: function(data, textStatus, errorThrown) {
149 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
149 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
150 }
150 }
151 })
151 })
152 }
152 }
153 })
153 })
154 });
154 });
155
155
156 });
156 });
157 </script>
157 </script>
@@ -1,71 +1,71 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Additional Email Addresses')}</h3>
5 <h3 class="panel-title">${_('Additional Email Addresses')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="emails_wrap">
8 <div class="emails_wrap">
9 <table class="rctable account_emails useremails">
9 <table class="rctable account_emails useremails">
10 <tr>
10 <tr>
11 <td class="td-user">
11 <td class="td-user">
12 ${base.gravatar(c.user.email, 16)}
12 ${base.gravatar(c.user.email, 16)}
13 <span class="user email">${c.user.email}</span>
13 <span class="user email">${c.user.email}</span>
14 </td>
14 </td>
15 <td class="td-tags">
15 <td class="td-tags">
16 <span class="tag">${_('Primary')}</span>
16 <span class="tag">${_('Primary')}</span>
17 </td>
17 </td>
18 </tr>
18 </tr>
19 %if c.user_email_map:
19 %if c.user_email_map:
20 %for em in c.user_email_map:
20 %for em in c.user_email_map:
21 <tr>
21 <tr>
22 <td class="td-user">
22 <td class="td-user">
23 ${base.gravatar(em.email, 16)}
23 ${base.gravatar(em.email, 16)}
24 <span class="user email">${em.email}</span>
24 <span class="user email">${em.email}</span>
25 </td>
25 </td>
26 <td class="td-action">
26 <td class="td-action">
27 ${h.secure_form(url('edit_user_emails', user_id=c.user.user_id),method='delete')}
27 ${h.secure_form(h.route_path('edit_user_emails_delete', user_id=c.user.user_id), method='POST')}
28 ${h.hidden('del_email_id',em.email_id)}
28 ${h.hidden('del_email_id', em.email_id)}
29 <button class="btn btn-link btn-danger" type="submit"
29 <button class="btn btn-link btn-danger" type="submit"
30 onclick="return confirm('${_('Confirm to delete this email: %s') % em.email}');">
30 onclick="return confirm('${_('Confirm to delete this email: %s') % em.email}');">
31 ${_('Delete')}
31 ${_('Delete')}
32 </button>
32 </button>
33 ${h.end_form()}
33 ${h.end_form()}
34 </td>
34 </td>
35 </tr>
35 </tr>
36 %endfor
36 %endfor
37 %else:
37 %else:
38 <tr class="noborder">
38 <tr class="noborder">
39 <td colspan="3">
39 <td colspan="3">
40 <div class="td-email">
40 <div class="td-email">
41 ${_('No additional emails specified')}
41 ${_('No additional emails specified')}
42 </div>
42 </div>
43 </td>
43 </td>
44 </tr>
44 </tr>
45 %endif
45 %endif
46 </table>
46 </table>
47 </div>
47 </div>
48
48
49 ${h.secure_form(url('edit_user_emails', user_id=c.user.user_id),method='put')}
49 ${h.secure_form(h.route_path('edit_user_emails_add', user_id=c.user.user_id), method='POST')}
50 <div class="form">
50 <div class="form">
51 <!-- fields -->
51 <!-- fields -->
52 <div class="fields">
52 <div class="fields">
53 <div class="field">
53 <div class="field">
54 <div class="label">
54 <div class="label">
55 <label for="new_email">${_('New email address')}:</label>
55 <label for="new_email">${_('New email address')}:</label>
56 </div>
56 </div>
57 <div class="input">
57 <div class="input">
58 ${h.text('new_email', class_='medium')}
58 ${h.text('new_email', class_='medium')}
59 </div>
59 </div>
60 </div>
60 </div>
61 <div class="buttons">
61 <div class="buttons">
62 ${h.submit('save',_('Add'),class_="btn btn-small")}
62 ${h.submit('save',_('Add'),class_="btn btn-small")}
63 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
63 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
64 </div>
64 </div>
65 </div>
65 </div>
66 </div>
66 </div>
67 ${h.end_form()}
67 ${h.end_form()}
68 </div>
68 </div>
69 </div>
69 </div>
70
70
71
71
@@ -1,78 +1,78 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Custom IP Whitelist')}</h3>
3 <h3 class="panel-title">${_('Custom IP Whitelist')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="ips_wrap">
6 <div class="ips_wrap">
7 <h5>${_('Current IP address')}: <code>${c.rhodecode_user.ip_addr}</code></h5>
7 <h5>${_('Current IP address')}: <code>${c.rhodecode_user.ip_addr}</code></h5>
8 <table class="rctable ip-whitelist">
8 <table class="rctable ip-whitelist">
9 <tr>
9 <tr>
10 <th>${_('IP Address')}</th>
10 <th>${_('IP Address')}</th>
11 <th>${_('IP Range')}</th>
11 <th>${_('IP Range')}</th>
12 <th>${_('Description')}</th>
12 <th>${_('Description')}</th>
13 <th></th>
13 <th></th>
14 </tr>
14 </tr>
15 %if c.default_user_ip_map and c.inherit_default_ips:
15 %if c.default_user_ip_map and c.inherit_default_ips:
16 %for ip in c.default_user_ip_map:
16 %for ip in c.default_user_ip_map:
17 <tr>
17 <tr>
18 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
18 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
19 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
19 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
20 <td class="td-description">${h.literal(_('Inherited from %s') % h.link_to('*default*',h.url('admin_permissions_ips')))}</td>
20 <td class="td-description">${h.literal(_('Inherited from %s') % h.link_to('*default*',h.url('admin_permissions_ips')))}</td>
21 <td></td>
21 <td></td>
22 </tr>
22 </tr>
23 %endfor
23 %endfor
24 %endif
24 %endif
25
25
26 %if c.user_ip_map:
26 %if c.user_ip_map:
27 %for ip in c.user_ip_map:
27 %for ip in c.user_ip_map:
28 <tr>
28 <tr>
29 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
29 <td class="td-ip"><div class="ip">${ip.ip_addr}</div></td>
30 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
30 <td class="td-iprange"><div class="ip">${h.ip_range(ip.ip_addr)}</div></td>
31 <td class="td-description"><div class="ip">${ip.description}</div></td>
31 <td class="td-description"><div class="ip">${ip.description}</div></td>
32 <td class="td-action">
32 <td class="td-action">
33 ${h.secure_form(url('edit_user_ips', user_id=c.user.user_id),method='delete')}
33 ${h.secure_form(h.route_path('edit_user_ips_delete', user_id=c.user.user_id), method='POST')}
34 ${h.hidden('del_ip_id',ip.ip_id)}
34 ${h.hidden('del_ip_id', ip.ip_id)}
35 ${h.submit('remove_',_('Delete'),id="remove_ip_%s" % ip.ip_id,
35 ${h.submit('remove_', _('Delete'),id="remove_ip_%s" % ip.ip_id,
36 class_="btn btn-link btn-danger", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
36 class_="btn btn-link btn-danger", onclick="return confirm('"+_('Confirm to delete this ip: %s') % ip.ip_addr+"');")}
37 ${h.end_form()}
37 ${h.end_form()}
38 </td>
38 </td>
39 </tr>
39 </tr>
40 %endfor
40 %endfor
41 %endif
41 %endif
42 %if not c.default_user_ip_map and not c.user_ip_map:
42 %if not c.default_user_ip_map and not c.user_ip_map:
43 <tr>
43 <tr>
44 <td><h2 class="ip">${_('All IP addresses are allowed')}</h2></td>
44 <td><h2 class="ip">${_('All IP addresses are allowed')}</h2></td>
45 <td></td>
45 <td></td>
46 <td></td>
46 <td></td>
47 <td></td>
47 <td></td>
48 </tr>
48 </tr>
49 %endif
49 %endif
50 </table>
50 </table>
51 </div>
51 </div>
52
52
53 <div>
53 <div>
54 ${h.secure_form(url('edit_user_ips', user_id=c.user.user_id),method='put')}
54 ${h.secure_form(h.route_path('edit_user_ips_add', user_id=c.user.user_id), method='POST')}
55 <div class="form">
55 <div class="form">
56 <!-- fields -->
56 <!-- fields -->
57 <div class="fields">
57 <div class="fields">
58 <div class="field">
58 <div class="field">
59 <div class="label">
59 <div class="label">
60 <label for="new_ip">${_('New IP Address')}:</label>
60 <label for="new_ip">${_('New IP Address')}:</label>
61 </div>
61 </div>
62 <div class="input">
62 <div class="input">
63 ${h.text('new_ip')} ${h.text('description', placeholder=_('Description...'))}
63 ${h.text('new_ip')} ${h.text('description', placeholder=_('Description...'))}
64 <span class="help-block">${_('Enter comma separated list of ip addresses like 127.0.0.1,\n'
64 <span class="help-block">${_('Enter comma separated list of ip addresses like 127.0.0.1,\n'
65 'or use a ip address with a mask 127.0.0.1/24, to create a network range.\n'
65 'or use a ip address with a mask 127.0.0.1/24, to create a network range.\n'
66 'To specify multiple address range use 127.0.0.1-127.0.0.10 syntax')}</span>
66 'To specify multiple address range use 127.0.0.1-127.0.0.10 syntax')}</span>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div class="buttons">
69 <div class="buttons">
70 ${h.submit('save',_('Add'),class_="btn btn-small")}
70 ${h.submit('save',_('Add'),class_="btn btn-small")}
71 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
71 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
72 </div>
72 </div>
73 </div>
73 </div>
74 </div>
74 </div>
75 ${h.end_form()}
75 ${h.end_form()}
76 </div>
76 </div>
77 </div>
77 </div>
78 </div>
78 </div>
@@ -1,213 +1,229 b''
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 import pytest
21 import pytest
22 from rhodecode.model.db import User, UserIpMap
22 from rhodecode.model.db import User, UserIpMap
23 from rhodecode.model.permission import PermissionModel
23 from rhodecode.model.permission import PermissionModel
24 from rhodecode.tests import (
24 from rhodecode.tests import (
25 TestController, url, clear_all_caches, assert_session_flash)
25 TestController, url, clear_all_caches, assert_session_flash)
26
26
27
27
28 def route_path(name, params=None, **kwargs):
29 import urllib
30 from rhodecode.apps._base import ADMIN_PREFIX
31
32 base_url = {
33 'edit_user_ips':
34 ADMIN_PREFIX + '/users/{user_id}/edit/ips',
35 'edit_user_ips_add':
36 ADMIN_PREFIX + '/users/{user_id}/edit/ips/new',
37 'edit_user_ips_delete':
38 ADMIN_PREFIX + '/users/{user_id}/edit/ips/delete',
39 }[name].format(**kwargs)
40
41 if params:
42 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
43 return base_url
44
45
28 class TestAdminPermissionsController(TestController):
46 class TestAdminPermissionsController(TestController):
29
47
30 @pytest.fixture(scope='class', autouse=True)
48 @pytest.fixture(scope='class', autouse=True)
31 def prepare(self, request):
49 def prepare(self, request):
32 # cleanup and reset to default permissions after
50 # cleanup and reset to default permissions after
33 @request.addfinalizer
51 @request.addfinalizer
34 def cleanup():
52 def cleanup():
35 PermissionModel().create_default_user_permissions(
53 PermissionModel().create_default_user_permissions(
36 User.get_default_user(), force=True)
54 User.get_default_user(), force=True)
37
55
38 def test_index_application(self):
56 def test_index_application(self):
39 self.log_user()
57 self.log_user()
40 self.app.get(url('admin_permissions_application'))
58 self.app.get(url('admin_permissions_application'))
41
59
42 @pytest.mark.parametrize(
60 @pytest.mark.parametrize(
43 'anonymous, default_register, default_register_message, default_password_reset,'
61 'anonymous, default_register, default_register_message, default_password_reset,'
44 'default_extern_activate, expect_error, expect_form_error', [
62 'default_extern_activate, expect_error, expect_form_error', [
45 (True, 'hg.register.none', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
63 (True, 'hg.register.none', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
46 False, False),
64 False, False),
47 (True, 'hg.register.manual_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.auto',
65 (True, 'hg.register.manual_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.auto',
48 False, False),
66 False, False),
49 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
67 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
50 False, False),
68 False, False),
51 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
69 (True, 'hg.register.auto_activate', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
52 False, False),
70 False, False),
53 (True, 'hg.register.XXX', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
71 (True, 'hg.register.XXX', '', 'hg.password_reset.enabled', 'hg.extern_activate.manual',
54 False, True),
72 False, True),
55 (True, '', '', 'hg.password_reset.enabled', '', True, False),
73 (True, '', '', 'hg.password_reset.enabled', '', True, False),
56 ])
74 ])
57 def test_update_application_permissions(
75 def test_update_application_permissions(
58 self, anonymous, default_register, default_register_message, default_password_reset,
76 self, anonymous, default_register, default_register_message, default_password_reset,
59 default_extern_activate, expect_error, expect_form_error):
77 default_extern_activate, expect_error, expect_form_error):
60
78
61 self.log_user()
79 self.log_user()
62
80
63 # TODO: anonymous access set here to False, breaks some other tests
81 # TODO: anonymous access set here to False, breaks some other tests
64 params = {
82 params = {
65 'csrf_token': self.csrf_token,
83 'csrf_token': self.csrf_token,
66 'anonymous': anonymous,
84 'anonymous': anonymous,
67 'default_register': default_register,
85 'default_register': default_register,
68 'default_register_message': default_register_message,
86 'default_register_message': default_register_message,
69 'default_password_reset': default_password_reset,
87 'default_password_reset': default_password_reset,
70 'default_extern_activate': default_extern_activate,
88 'default_extern_activate': default_extern_activate,
71 }
89 }
72 response = self.app.post(url('admin_permissions_application'),
90 response = self.app.post(url('admin_permissions_application'),
73 params=params)
91 params=params)
74 if expect_form_error:
92 if expect_form_error:
75 assert response.status_int == 200
93 assert response.status_int == 200
76 response.mustcontain('Value must be one of')
94 response.mustcontain('Value must be one of')
77 else:
95 else:
78 if expect_error:
96 if expect_error:
79 msg = 'Error occurred during update of permissions'
97 msg = 'Error occurred during update of permissions'
80 else:
98 else:
81 msg = 'Application permissions updated successfully'
99 msg = 'Application permissions updated successfully'
82 assert_session_flash(response, msg)
100 assert_session_flash(response, msg)
83
101
84 def test_index_object(self):
102 def test_index_object(self):
85 self.log_user()
103 self.log_user()
86 self.app.get(url('admin_permissions_object'))
104 self.app.get(url('admin_permissions_object'))
87
105
88 @pytest.mark.parametrize(
106 @pytest.mark.parametrize(
89 'repo, repo_group, user_group, expect_error, expect_form_error', [
107 'repo, repo_group, user_group, expect_error, expect_form_error', [
90 ('repository.none', 'group.none', 'usergroup.none', False, False),
108 ('repository.none', 'group.none', 'usergroup.none', False, False),
91 ('repository.read', 'group.read', 'usergroup.read', False, False),
109 ('repository.read', 'group.read', 'usergroup.read', False, False),
92 ('repository.write', 'group.write', 'usergroup.write',
110 ('repository.write', 'group.write', 'usergroup.write',
93 False, False),
111 False, False),
94 ('repository.admin', 'group.admin', 'usergroup.admin',
112 ('repository.admin', 'group.admin', 'usergroup.admin',
95 False, False),
113 False, False),
96 ('repository.XXX', 'group.admin', 'usergroup.admin', False, True),
114 ('repository.XXX', 'group.admin', 'usergroup.admin', False, True),
97 ('', '', '', True, False),
115 ('', '', '', True, False),
98 ])
116 ])
99 def test_update_object_permissions(self, repo, repo_group, user_group,
117 def test_update_object_permissions(self, repo, repo_group, user_group,
100 expect_error, expect_form_error):
118 expect_error, expect_form_error):
101 self.log_user()
119 self.log_user()
102
120
103 params = {
121 params = {
104 'csrf_token': self.csrf_token,
122 'csrf_token': self.csrf_token,
105 'default_repo_perm': repo,
123 'default_repo_perm': repo,
106 'overwrite_default_repo': False,
124 'overwrite_default_repo': False,
107 'default_group_perm': repo_group,
125 'default_group_perm': repo_group,
108 'overwrite_default_group': False,
126 'overwrite_default_group': False,
109 'default_user_group_perm': user_group,
127 'default_user_group_perm': user_group,
110 'overwrite_default_user_group': False,
128 'overwrite_default_user_group': False,
111 }
129 }
112 response = self.app.post(url('admin_permissions_object'),
130 response = self.app.post(url('admin_permissions_object'),
113 params=params)
131 params=params)
114 if expect_form_error:
132 if expect_form_error:
115 assert response.status_int == 200
133 assert response.status_int == 200
116 response.mustcontain('Value must be one of')
134 response.mustcontain('Value must be one of')
117 else:
135 else:
118 if expect_error:
136 if expect_error:
119 msg = 'Error occurred during update of permissions'
137 msg = 'Error occurred during update of permissions'
120 else:
138 else:
121 msg = 'Object permissions updated successfully'
139 msg = 'Object permissions updated successfully'
122 assert_session_flash(response, msg)
140 assert_session_flash(response, msg)
123
141
124 def test_index_global(self):
142 def test_index_global(self):
125 self.log_user()
143 self.log_user()
126 self.app.get(url('admin_permissions_global'))
144 self.app.get(url('admin_permissions_global'))
127
145
128 @pytest.mark.parametrize(
146 @pytest.mark.parametrize(
129 'repo_create, repo_create_write, user_group_create, repo_group_create,'
147 'repo_create, repo_create_write, user_group_create, repo_group_create,'
130 'fork_create, inherit_default_permissions, expect_error,'
148 'fork_create, inherit_default_permissions, expect_error,'
131 'expect_form_error', [
149 'expect_form_error', [
132 ('hg.create.none', 'hg.create.write_on_repogroup.false',
150 ('hg.create.none', 'hg.create.write_on_repogroup.false',
133 'hg.usergroup.create.false', 'hg.repogroup.create.false',
151 'hg.usergroup.create.false', 'hg.repogroup.create.false',
134 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
152 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
135 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
153 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
136 'hg.usergroup.create.true', 'hg.repogroup.create.true',
154 'hg.usergroup.create.true', 'hg.repogroup.create.true',
137 'hg.fork.repository', 'hg.inherit_default_perms.false',
155 'hg.fork.repository', 'hg.inherit_default_perms.false',
138 False, False),
156 False, False),
139 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
157 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
140 'hg.usergroup.create.true', 'hg.repogroup.create.true',
158 'hg.usergroup.create.true', 'hg.repogroup.create.true',
141 'hg.fork.repository', 'hg.inherit_default_perms.false',
159 'hg.fork.repository', 'hg.inherit_default_perms.false',
142 False, True),
160 False, True),
143 ('', '', '', '', '', '', True, False),
161 ('', '', '', '', '', '', True, False),
144 ])
162 ])
145 def test_update_global_permissions(
163 def test_update_global_permissions(
146 self, repo_create, repo_create_write, user_group_create,
164 self, repo_create, repo_create_write, user_group_create,
147 repo_group_create, fork_create, inherit_default_permissions,
165 repo_group_create, fork_create, inherit_default_permissions,
148 expect_error, expect_form_error):
166 expect_error, expect_form_error):
149 self.log_user()
167 self.log_user()
150
168
151 params = {
169 params = {
152 'csrf_token': self.csrf_token,
170 'csrf_token': self.csrf_token,
153 'default_repo_create': repo_create,
171 'default_repo_create': repo_create,
154 'default_repo_create_on_write': repo_create_write,
172 'default_repo_create_on_write': repo_create_write,
155 'default_user_group_create': user_group_create,
173 'default_user_group_create': user_group_create,
156 'default_repo_group_create': repo_group_create,
174 'default_repo_group_create': repo_group_create,
157 'default_fork_create': fork_create,
175 'default_fork_create': fork_create,
158 'default_inherit_default_permissions': inherit_default_permissions
176 'default_inherit_default_permissions': inherit_default_permissions
159 }
177 }
160 response = self.app.post(url('admin_permissions_global'),
178 response = self.app.post(url('admin_permissions_global'),
161 params=params)
179 params=params)
162 if expect_form_error:
180 if expect_form_error:
163 assert response.status_int == 200
181 assert response.status_int == 200
164 response.mustcontain('Value must be one of')
182 response.mustcontain('Value must be one of')
165 else:
183 else:
166 if expect_error:
184 if expect_error:
167 msg = 'Error occurred during update of permissions'
185 msg = 'Error occurred during update of permissions'
168 else:
186 else:
169 msg = 'Global permissions updated successfully'
187 msg = 'Global permissions updated successfully'
170 assert_session_flash(response, msg)
188 assert_session_flash(response, msg)
171
189
172 def test_index_ips(self):
190 def test_index_ips(self):
173 self.log_user()
191 self.log_user()
174 response = self.app.get(url('admin_permissions_ips'))
192 response = self.app.get(url('admin_permissions_ips'))
175 # TODO: Test response...
193 # TODO: Test response...
176 response.mustcontain('All IP addresses are allowed')
194 response.mustcontain('All IP addresses are allowed')
177
195
178 def test_add_delete_ips(self):
196 def test_add_delete_ips(self):
179 self.log_user()
197 self.log_user()
180 clear_all_caches()
198 clear_all_caches()
181
199
182 # ADD
200 # ADD
183 default_user_id = User.get_default_user().user_id
201 default_user_id = User.get_default_user().user_id
184 response = self.app.post(
202 self.app.post(
185 url('edit_user_ips', user_id=default_user_id),
203 route_path('edit_user_ips_add', user_id=default_user_id),
186 params={'new_ip': '127.0.0.0/24', '_method': 'put',
204 params={'new_ip': '127.0.0.0/24', 'csrf_token': self.csrf_token})
187 'csrf_token': self.csrf_token})
188
205
189 response = self.app.get(url('admin_permissions_ips'))
206 response = self.app.get(url('admin_permissions_ips'))
190 response.mustcontain('127.0.0.0/24')
207 response.mustcontain('127.0.0.0/24')
191 response.mustcontain('127.0.0.0 - 127.0.0.255')
208 response.mustcontain('127.0.0.0 - 127.0.0.255')
192
209
193 # DELETE
210 # DELETE
194 default_user_id = User.get_default_user().user_id
211 default_user_id = User.get_default_user().user_id
195 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
212 del_ip_id = UserIpMap.query().filter(UserIpMap.user_id ==
196 default_user_id).first().ip_id
213 default_user_id).first().ip_id
197
214
198 response = self.app.post(
215 response = self.app.post(
199 url('edit_user_ips', user_id=default_user_id),
216 route_path('edit_user_ips_delete', user_id=default_user_id),
200 params={'_method': 'delete', 'del_ip_id': del_ip_id,
217 params={'del_ip_id': del_ip_id, 'csrf_token': self.csrf_token})
201 'csrf_token': self.csrf_token})
202
218
203 assert_session_flash(response, 'Removed ip address from user whitelist')
219 assert_session_flash(response, 'Removed ip address from user whitelist')
204
220
205 clear_all_caches()
221 clear_all_caches()
206 response = self.app.get(url('admin_permissions_ips'))
222 response = self.app.get(url('admin_permissions_ips'))
207 response.mustcontain('All IP addresses are allowed')
223 response.mustcontain('All IP addresses are allowed')
208 response.mustcontain(no=['127.0.0.0/24'])
224 response.mustcontain(no=['127.0.0.0/24'])
209 response.mustcontain(no=['127.0.0.0 - 127.0.0.255'])
225 response.mustcontain(no=['127.0.0.0 - 127.0.0.255'])
210
226
211 def test_index_overview(self):
227 def test_index_overview(self):
212 self.log_user()
228 self.log_user()
213 self.app.get(url('admin_permissions_overview'))
229 self.app.get(url('admin_permissions_overview'))
@@ -1,575 +1,512 b''
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 import pytest
21 import pytest
22 from sqlalchemy.orm.exc import NoResultFound
22 from sqlalchemy.orm.exc import NoResultFound
23
23
24 from rhodecode.lib.auth import check_password
24 from rhodecode.lib.auth import check_password
25 from rhodecode.lib import helpers as h
25 from rhodecode.lib import helpers as h
26 from rhodecode.model import validators
26 from rhodecode.model import validators
27 from rhodecode.model.db import User, UserIpMap, UserApiKeys
27 from rhodecode.model.db import User, UserIpMap, UserApiKeys
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.user import UserModel
29 from rhodecode.model.user import UserModel
30 from rhodecode.tests import (
30 from rhodecode.tests import (
31 TestController, url, link_to, TEST_USER_ADMIN_LOGIN,
31 TestController, url, link_to, TEST_USER_ADMIN_LOGIN,
32 TEST_USER_REGULAR_LOGIN, assert_session_flash)
32 TEST_USER_REGULAR_LOGIN, assert_session_flash)
33 from rhodecode.tests.fixture import Fixture
33 from rhodecode.tests.fixture import Fixture
34 from rhodecode.tests.utils import AssertResponse
34 from rhodecode.tests.utils import AssertResponse
35
35
36 fixture = Fixture()
36 fixture = Fixture()
37
37
38
38
39 def route_path(name, params=None, **kwargs):
39 def route_path(name, params=None, **kwargs):
40 import urllib
40 import urllib
41 from rhodecode.apps._base import ADMIN_PREFIX
41 from rhodecode.apps._base import ADMIN_PREFIX
42
42
43 base_url = {
43 base_url = {
44 'users_data':
44 'users_data':
45 ADMIN_PREFIX + '/users_data',
45 ADMIN_PREFIX + '/users_data',
46 }[name].format(**kwargs)
46 }[name].format(**kwargs)
47
47
48 if params:
48 if params:
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
50 return base_url
50 return base_url
51
51
52
52
53 class TestAdminUsersController(TestController):
53 class TestAdminUsersController(TestController):
54 test_user_1 = 'testme'
54 test_user_1 = 'testme'
55 destroy_users = set()
55 destroy_users = set()
56
56
57 @classmethod
57 @classmethod
58 def teardown_method(cls, method):
58 def teardown_method(cls, method):
59 fixture.destroy_users(cls.destroy_users)
59 fixture.destroy_users(cls.destroy_users)
60
60
61 def test_create(self, xhr_header):
61 def test_create(self, xhr_header):
62 self.log_user()
62 self.log_user()
63 username = 'newtestuser'
63 username = 'newtestuser'
64 password = 'test12'
64 password = 'test12'
65 password_confirmation = password
65 password_confirmation = password
66 name = 'name'
66 name = 'name'
67 lastname = 'lastname'
67 lastname = 'lastname'
68 email = 'mail@mail.com'
68 email = 'mail@mail.com'
69
69
70 self.app.get(url('new_user'))
70 self.app.get(url('new_user'))
71
71
72 response = self.app.post(url('users'), params={
72 response = self.app.post(url('users'), params={
73 'username': username,
73 'username': username,
74 'password': password,
74 'password': password,
75 'password_confirmation': password_confirmation,
75 'password_confirmation': password_confirmation,
76 'firstname': name,
76 'firstname': name,
77 'active': True,
77 'active': True,
78 'lastname': lastname,
78 'lastname': lastname,
79 'extern_name': 'rhodecode',
79 'extern_name': 'rhodecode',
80 'extern_type': 'rhodecode',
80 'extern_type': 'rhodecode',
81 'email': email,
81 'email': email,
82 'csrf_token': self.csrf_token,
82 'csrf_token': self.csrf_token,
83 })
83 })
84 user_link = link_to(
84 user_link = link_to(
85 username,
85 username,
86 url('edit_user', user_id=User.get_by_username(username).user_id))
86 url('edit_user', user_id=User.get_by_username(username).user_id))
87 assert_session_flash(response, 'Created user %s' % (user_link,))
87 assert_session_flash(response, 'Created user %s' % (user_link,))
88 self.destroy_users.add(username)
88 self.destroy_users.add(username)
89
89
90 new_user = User.query().filter(User.username == username).one()
90 new_user = User.query().filter(User.username == username).one()
91
91
92 assert new_user.username == username
92 assert new_user.username == username
93 assert check_password(password, new_user.password)
93 assert check_password(password, new_user.password)
94 assert new_user.name == name
94 assert new_user.name == name
95 assert new_user.lastname == lastname
95 assert new_user.lastname == lastname
96 assert new_user.email == email
96 assert new_user.email == email
97
97
98 response = self.app.get(route_path('users_data'),
98 response = self.app.get(route_path('users_data'),
99 extra_environ=xhr_header)
99 extra_environ=xhr_header)
100 response.mustcontain(username)
100 response.mustcontain(username)
101
101
102 def test_create_err(self):
102 def test_create_err(self):
103 self.log_user()
103 self.log_user()
104 username = 'new_user'
104 username = 'new_user'
105 password = ''
105 password = ''
106 name = 'name'
106 name = 'name'
107 lastname = 'lastname'
107 lastname = 'lastname'
108 email = 'errmail.com'
108 email = 'errmail.com'
109
109
110 self.app.get(url('new_user'))
110 self.app.get(url('new_user'))
111
111
112 response = self.app.post(url('users'), params={
112 response = self.app.post(url('users'), params={
113 'username': username,
113 'username': username,
114 'password': password,
114 'password': password,
115 'name': name,
115 'name': name,
116 'active': False,
116 'active': False,
117 'lastname': lastname,
117 'lastname': lastname,
118 'email': email,
118 'email': email,
119 'csrf_token': self.csrf_token,
119 'csrf_token': self.csrf_token,
120 })
120 })
121
121
122 msg = validators.ValidUsername(
122 msg = validators.ValidUsername(
123 False, {})._messages['system_invalid_username']
123 False, {})._messages['system_invalid_username']
124 msg = h.html_escape(msg % {'username': 'new_user'})
124 msg = h.html_escape(msg % {'username': 'new_user'})
125 response.mustcontain('<span class="error-message">%s</span>' % msg)
125 response.mustcontain('<span class="error-message">%s</span>' % msg)
126 response.mustcontain(
126 response.mustcontain(
127 '<span class="error-message">Please enter a value</span>')
127 '<span class="error-message">Please enter a value</span>')
128 response.mustcontain(
128 response.mustcontain(
129 '<span class="error-message">An email address must contain a'
129 '<span class="error-message">An email address must contain a'
130 ' single @</span>')
130 ' single @</span>')
131
131
132 def get_user():
132 def get_user():
133 Session().query(User).filter(User.username == username).one()
133 Session().query(User).filter(User.username == username).one()
134
134
135 with pytest.raises(NoResultFound):
135 with pytest.raises(NoResultFound):
136 get_user()
136 get_user()
137
137
138 def test_new(self):
138 def test_new(self):
139 self.log_user()
139 self.log_user()
140 self.app.get(url('new_user'))
140 self.app.get(url('new_user'))
141
141
142 @pytest.mark.parametrize("name, attrs", [
142 @pytest.mark.parametrize("name, attrs", [
143 ('firstname', {'firstname': 'new_username'}),
143 ('firstname', {'firstname': 'new_username'}),
144 ('lastname', {'lastname': 'new_username'}),
144 ('lastname', {'lastname': 'new_username'}),
145 ('admin', {'admin': True}),
145 ('admin', {'admin': True}),
146 ('admin', {'admin': False}),
146 ('admin', {'admin': False}),
147 ('extern_type', {'extern_type': 'ldap'}),
147 ('extern_type', {'extern_type': 'ldap'}),
148 ('extern_type', {'extern_type': None}),
148 ('extern_type', {'extern_type': None}),
149 ('extern_name', {'extern_name': 'test'}),
149 ('extern_name', {'extern_name': 'test'}),
150 ('extern_name', {'extern_name': None}),
150 ('extern_name', {'extern_name': None}),
151 ('active', {'active': False}),
151 ('active', {'active': False}),
152 ('active', {'active': True}),
152 ('active', {'active': True}),
153 ('email', {'email': 'some@email.com'}),
153 ('email', {'email': 'some@email.com'}),
154 ('language', {'language': 'de'}),
154 ('language', {'language': 'de'}),
155 ('language', {'language': 'en'}),
155 ('language', {'language': 'en'}),
156 # ('new_password', {'new_password': 'foobar123',
156 # ('new_password', {'new_password': 'foobar123',
157 # 'password_confirmation': 'foobar123'})
157 # 'password_confirmation': 'foobar123'})
158 ])
158 ])
159 def test_update(self, name, attrs):
159 def test_update(self, name, attrs):
160 self.log_user()
160 self.log_user()
161 usr = fixture.create_user(self.test_user_1, password='qweqwe',
161 usr = fixture.create_user(self.test_user_1, password='qweqwe',
162 email='testme@rhodecode.org',
162 email='testme@rhodecode.org',
163 extern_type='rhodecode',
163 extern_type='rhodecode',
164 extern_name=self.test_user_1,
164 extern_name=self.test_user_1,
165 skip_if_exists=True)
165 skip_if_exists=True)
166 Session().commit()
166 Session().commit()
167 self.destroy_users.add(self.test_user_1)
167 self.destroy_users.add(self.test_user_1)
168 params = usr.get_api_data()
168 params = usr.get_api_data()
169 cur_lang = params['language'] or 'en'
169 cur_lang = params['language'] or 'en'
170 params.update({
170 params.update({
171 'password_confirmation': '',
171 'password_confirmation': '',
172 'new_password': '',
172 'new_password': '',
173 'language': cur_lang,
173 'language': cur_lang,
174 '_method': 'put',
174 '_method': 'put',
175 'csrf_token': self.csrf_token,
175 'csrf_token': self.csrf_token,
176 })
176 })
177 params.update({'new_password': ''})
177 params.update({'new_password': ''})
178 params.update(attrs)
178 params.update(attrs)
179 if name == 'email':
179 if name == 'email':
180 params['emails'] = [attrs['email']]
180 params['emails'] = [attrs['email']]
181 elif name == 'extern_type':
181 elif name == 'extern_type':
182 # cannot update this via form, expected value is original one
182 # cannot update this via form, expected value is original one
183 params['extern_type'] = "rhodecode"
183 params['extern_type'] = "rhodecode"
184 elif name == 'extern_name':
184 elif name == 'extern_name':
185 # cannot update this via form, expected value is original one
185 # cannot update this via form, expected value is original one
186 params['extern_name'] = self.test_user_1
186 params['extern_name'] = self.test_user_1
187 # special case since this user is not
187 # special case since this user is not
188 # logged in yet his data is not filled
188 # logged in yet his data is not filled
189 # so we use creation data
189 # so we use creation data
190
190
191 response = self.app.post(url('user', user_id=usr.user_id), params)
191 response = self.app.post(url('user', user_id=usr.user_id), params)
192 assert response.status_int == 302
192 assert response.status_int == 302
193 assert_session_flash(response, 'User updated successfully')
193 assert_session_flash(response, 'User updated successfully')
194
194
195 updated_user = User.get_by_username(self.test_user_1)
195 updated_user = User.get_by_username(self.test_user_1)
196 updated_params = updated_user.get_api_data()
196 updated_params = updated_user.get_api_data()
197 updated_params.update({'password_confirmation': ''})
197 updated_params.update({'password_confirmation': ''})
198 updated_params.update({'new_password': ''})
198 updated_params.update({'new_password': ''})
199
199
200 del params['_method']
200 del params['_method']
201 del params['csrf_token']
201 del params['csrf_token']
202 assert params == updated_params
202 assert params == updated_params
203
203
204 def test_update_and_migrate_password(
204 def test_update_and_migrate_password(
205 self, autologin_user, real_crypto_backend):
205 self, autologin_user, real_crypto_backend):
206 from rhodecode.lib import auth
206 from rhodecode.lib import auth
207
207
208 # create new user, with sha256 password
208 # create new user, with sha256 password
209 temp_user = 'test_admin_sha256'
209 temp_user = 'test_admin_sha256'
210 user = fixture.create_user(temp_user)
210 user = fixture.create_user(temp_user)
211 user.password = auth._RhodeCodeCryptoSha256().hash_create(
211 user.password = auth._RhodeCodeCryptoSha256().hash_create(
212 b'test123')
212 b'test123')
213 Session().add(user)
213 Session().add(user)
214 Session().commit()
214 Session().commit()
215 self.destroy_users.add('test_admin_sha256')
215 self.destroy_users.add('test_admin_sha256')
216
216
217 params = user.get_api_data()
217 params = user.get_api_data()
218
218
219 params.update({
219 params.update({
220 'password_confirmation': 'qweqwe123',
220 'password_confirmation': 'qweqwe123',
221 'new_password': 'qweqwe123',
221 'new_password': 'qweqwe123',
222 'language': 'en',
222 'language': 'en',
223 '_method': 'put',
223 '_method': 'put',
224 'csrf_token': autologin_user.csrf_token,
224 'csrf_token': autologin_user.csrf_token,
225 })
225 })
226
226
227 response = self.app.post(url('user', user_id=user.user_id), params)
227 response = self.app.post(url('user', user_id=user.user_id), params)
228 assert response.status_int == 302
228 assert response.status_int == 302
229 assert_session_flash(response, 'User updated successfully')
229 assert_session_flash(response, 'User updated successfully')
230
230
231 # new password should be bcrypted, after log-in and transfer
231 # new password should be bcrypted, after log-in and transfer
232 user = User.get_by_username(temp_user)
232 user = User.get_by_username(temp_user)
233 assert user.password.startswith('$')
233 assert user.password.startswith('$')
234
234
235 updated_user = User.get_by_username(temp_user)
235 updated_user = User.get_by_username(temp_user)
236 updated_params = updated_user.get_api_data()
236 updated_params = updated_user.get_api_data()
237 updated_params.update({'password_confirmation': 'qweqwe123'})
237 updated_params.update({'password_confirmation': 'qweqwe123'})
238 updated_params.update({'new_password': 'qweqwe123'})
238 updated_params.update({'new_password': 'qweqwe123'})
239
239
240 del params['_method']
240 del params['_method']
241 del params['csrf_token']
241 del params['csrf_token']
242 assert params == updated_params
242 assert params == updated_params
243
243
244 def test_delete(self):
244 def test_delete(self):
245 self.log_user()
245 self.log_user()
246 username = 'newtestuserdeleteme'
246 username = 'newtestuserdeleteme'
247
247
248 fixture.create_user(name=username)
248 fixture.create_user(name=username)
249
249
250 new_user = Session().query(User)\
250 new_user = Session().query(User)\
251 .filter(User.username == username).one()
251 .filter(User.username == username).one()
252 response = self.app.post(url('user', user_id=new_user.user_id),
252 response = self.app.post(url('user', user_id=new_user.user_id),
253 params={'_method': 'delete',
253 params={'_method': 'delete',
254 'csrf_token': self.csrf_token})
254 'csrf_token': self.csrf_token})
255
255
256 assert_session_flash(response, 'Successfully deleted user')
256 assert_session_flash(response, 'Successfully deleted user')
257
257
258 def test_delete_owner_of_repository(self):
258 def test_delete_owner_of_repository(self):
259 self.log_user()
259 self.log_user()
260 username = 'newtestuserdeleteme_repo_owner'
260 username = 'newtestuserdeleteme_repo_owner'
261 obj_name = 'test_repo'
261 obj_name = 'test_repo'
262 usr = fixture.create_user(name=username)
262 usr = fixture.create_user(name=username)
263 self.destroy_users.add(username)
263 self.destroy_users.add(username)
264 fixture.create_repo(obj_name, cur_user=usr.username)
264 fixture.create_repo(obj_name, cur_user=usr.username)
265
265
266 new_user = Session().query(User)\
266 new_user = Session().query(User)\
267 .filter(User.username == username).one()
267 .filter(User.username == username).one()
268 response = self.app.post(url('user', user_id=new_user.user_id),
268 response = self.app.post(url('user', user_id=new_user.user_id),
269 params={'_method': 'delete',
269 params={'_method': 'delete',
270 'csrf_token': self.csrf_token})
270 'csrf_token': self.csrf_token})
271
271
272 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
272 msg = 'user "%s" still owns 1 repositories and cannot be removed. ' \
273 'Switch owners or remove those repositories:%s' % (username,
273 'Switch owners or remove those repositories:%s' % (username,
274 obj_name)
274 obj_name)
275 assert_session_flash(response, msg)
275 assert_session_flash(response, msg)
276 fixture.destroy_repo(obj_name)
276 fixture.destroy_repo(obj_name)
277
277
278 def test_delete_owner_of_repository_detaching(self):
278 def test_delete_owner_of_repository_detaching(self):
279 self.log_user()
279 self.log_user()
280 username = 'newtestuserdeleteme_repo_owner_detach'
280 username = 'newtestuserdeleteme_repo_owner_detach'
281 obj_name = 'test_repo'
281 obj_name = 'test_repo'
282 usr = fixture.create_user(name=username)
282 usr = fixture.create_user(name=username)
283 self.destroy_users.add(username)
283 self.destroy_users.add(username)
284 fixture.create_repo(obj_name, cur_user=usr.username)
284 fixture.create_repo(obj_name, cur_user=usr.username)
285
285
286 new_user = Session().query(User)\
286 new_user = Session().query(User)\
287 .filter(User.username == username).one()
287 .filter(User.username == username).one()
288 response = self.app.post(url('user', user_id=new_user.user_id),
288 response = self.app.post(url('user', user_id=new_user.user_id),
289 params={'_method': 'delete',
289 params={'_method': 'delete',
290 'user_repos': 'detach',
290 'user_repos': 'detach',
291 'csrf_token': self.csrf_token})
291 'csrf_token': self.csrf_token})
292
292
293 msg = 'Detached 1 repositories'
293 msg = 'Detached 1 repositories'
294 assert_session_flash(response, msg)
294 assert_session_flash(response, msg)
295 fixture.destroy_repo(obj_name)
295 fixture.destroy_repo(obj_name)
296
296
297 def test_delete_owner_of_repository_deleting(self):
297 def test_delete_owner_of_repository_deleting(self):
298 self.log_user()
298 self.log_user()
299 username = 'newtestuserdeleteme_repo_owner_delete'
299 username = 'newtestuserdeleteme_repo_owner_delete'
300 obj_name = 'test_repo'
300 obj_name = 'test_repo'
301 usr = fixture.create_user(name=username)
301 usr = fixture.create_user(name=username)
302 self.destroy_users.add(username)
302 self.destroy_users.add(username)
303 fixture.create_repo(obj_name, cur_user=usr.username)
303 fixture.create_repo(obj_name, cur_user=usr.username)
304
304
305 new_user = Session().query(User)\
305 new_user = Session().query(User)\
306 .filter(User.username == username).one()
306 .filter(User.username == username).one()
307 response = self.app.post(url('user', user_id=new_user.user_id),
307 response = self.app.post(url('user', user_id=new_user.user_id),
308 params={'_method': 'delete',
308 params={'_method': 'delete',
309 'user_repos': 'delete',
309 'user_repos': 'delete',
310 'csrf_token': self.csrf_token})
310 'csrf_token': self.csrf_token})
311
311
312 msg = 'Deleted 1 repositories'
312 msg = 'Deleted 1 repositories'
313 assert_session_flash(response, msg)
313 assert_session_flash(response, msg)
314
314
315 def test_delete_owner_of_repository_group(self):
315 def test_delete_owner_of_repository_group(self):
316 self.log_user()
316 self.log_user()
317 username = 'newtestuserdeleteme_repo_group_owner'
317 username = 'newtestuserdeleteme_repo_group_owner'
318 obj_name = 'test_group'
318 obj_name = 'test_group'
319 usr = fixture.create_user(name=username)
319 usr = fixture.create_user(name=username)
320 self.destroy_users.add(username)
320 self.destroy_users.add(username)
321 fixture.create_repo_group(obj_name, cur_user=usr.username)
321 fixture.create_repo_group(obj_name, cur_user=usr.username)
322
322
323 new_user = Session().query(User)\
323 new_user = Session().query(User)\
324 .filter(User.username == username).one()
324 .filter(User.username == username).one()
325 response = self.app.post(url('user', user_id=new_user.user_id),
325 response = self.app.post(url('user', user_id=new_user.user_id),
326 params={'_method': 'delete',
326 params={'_method': 'delete',
327 'csrf_token': self.csrf_token})
327 'csrf_token': self.csrf_token})
328
328
329 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
329 msg = 'user "%s" still owns 1 repository groups and cannot be removed. ' \
330 'Switch owners or remove those repository groups:%s' % (username,
330 'Switch owners or remove those repository groups:%s' % (username,
331 obj_name)
331 obj_name)
332 assert_session_flash(response, msg)
332 assert_session_flash(response, msg)
333 fixture.destroy_repo_group(obj_name)
333 fixture.destroy_repo_group(obj_name)
334
334
335 def test_delete_owner_of_repository_group_detaching(self):
335 def test_delete_owner_of_repository_group_detaching(self):
336 self.log_user()
336 self.log_user()
337 username = 'newtestuserdeleteme_repo_group_owner_detach'
337 username = 'newtestuserdeleteme_repo_group_owner_detach'
338 obj_name = 'test_group'
338 obj_name = 'test_group'
339 usr = fixture.create_user(name=username)
339 usr = fixture.create_user(name=username)
340 self.destroy_users.add(username)
340 self.destroy_users.add(username)
341 fixture.create_repo_group(obj_name, cur_user=usr.username)
341 fixture.create_repo_group(obj_name, cur_user=usr.username)
342
342
343 new_user = Session().query(User)\
343 new_user = Session().query(User)\
344 .filter(User.username == username).one()
344 .filter(User.username == username).one()
345 response = self.app.post(url('user', user_id=new_user.user_id),
345 response = self.app.post(url('user', user_id=new_user.user_id),
346 params={'_method': 'delete',
346 params={'_method': 'delete',
347 'user_repo_groups': 'delete',
347 'user_repo_groups': 'delete',
348 'csrf_token': self.csrf_token})
348 'csrf_token': self.csrf_token})
349
349
350 msg = 'Deleted 1 repository groups'
350 msg = 'Deleted 1 repository groups'
351 assert_session_flash(response, msg)
351 assert_session_flash(response, msg)
352
352
353 def test_delete_owner_of_repository_group_deleting(self):
353 def test_delete_owner_of_repository_group_deleting(self):
354 self.log_user()
354 self.log_user()
355 username = 'newtestuserdeleteme_repo_group_owner_delete'
355 username = 'newtestuserdeleteme_repo_group_owner_delete'
356 obj_name = 'test_group'
356 obj_name = 'test_group'
357 usr = fixture.create_user(name=username)
357 usr = fixture.create_user(name=username)
358 self.destroy_users.add(username)
358 self.destroy_users.add(username)
359 fixture.create_repo_group(obj_name, cur_user=usr.username)
359 fixture.create_repo_group(obj_name, cur_user=usr.username)
360
360
361 new_user = Session().query(User)\
361 new_user = Session().query(User)\
362 .filter(User.username == username).one()
362 .filter(User.username == username).one()
363 response = self.app.post(url('user', user_id=new_user.user_id),
363 response = self.app.post(url('user', user_id=new_user.user_id),
364 params={'_method': 'delete',
364 params={'_method': 'delete',
365 'user_repo_groups': 'detach',
365 'user_repo_groups': 'detach',
366 'csrf_token': self.csrf_token})
366 'csrf_token': self.csrf_token})
367
367
368 msg = 'Detached 1 repository groups'
368 msg = 'Detached 1 repository groups'
369 assert_session_flash(response, msg)
369 assert_session_flash(response, msg)
370 fixture.destroy_repo_group(obj_name)
370 fixture.destroy_repo_group(obj_name)
371
371
372 def test_delete_owner_of_user_group(self):
372 def test_delete_owner_of_user_group(self):
373 self.log_user()
373 self.log_user()
374 username = 'newtestuserdeleteme_user_group_owner'
374 username = 'newtestuserdeleteme_user_group_owner'
375 obj_name = 'test_user_group'
375 obj_name = 'test_user_group'
376 usr = fixture.create_user(name=username)
376 usr = fixture.create_user(name=username)
377 self.destroy_users.add(username)
377 self.destroy_users.add(username)
378 fixture.create_user_group(obj_name, cur_user=usr.username)
378 fixture.create_user_group(obj_name, cur_user=usr.username)
379
379
380 new_user = Session().query(User)\
380 new_user = Session().query(User)\
381 .filter(User.username == username).one()
381 .filter(User.username == username).one()
382 response = self.app.post(url('user', user_id=new_user.user_id),
382 response = self.app.post(url('user', user_id=new_user.user_id),
383 params={'_method': 'delete',
383 params={'_method': 'delete',
384 'csrf_token': self.csrf_token})
384 'csrf_token': self.csrf_token})
385
385
386 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
386 msg = 'user "%s" still owns 1 user groups and cannot be removed. ' \
387 'Switch owners or remove those user groups:%s' % (username,
387 'Switch owners or remove those user groups:%s' % (username,
388 obj_name)
388 obj_name)
389 assert_session_flash(response, msg)
389 assert_session_flash(response, msg)
390 fixture.destroy_user_group(obj_name)
390 fixture.destroy_user_group(obj_name)
391
391
392 def test_delete_owner_of_user_group_detaching(self):
392 def test_delete_owner_of_user_group_detaching(self):
393 self.log_user()
393 self.log_user()
394 username = 'newtestuserdeleteme_user_group_owner_detaching'
394 username = 'newtestuserdeleteme_user_group_owner_detaching'
395 obj_name = 'test_user_group'
395 obj_name = 'test_user_group'
396 usr = fixture.create_user(name=username)
396 usr = fixture.create_user(name=username)
397 self.destroy_users.add(username)
397 self.destroy_users.add(username)
398 fixture.create_user_group(obj_name, cur_user=usr.username)
398 fixture.create_user_group(obj_name, cur_user=usr.username)
399
399
400 new_user = Session().query(User)\
400 new_user = Session().query(User)\
401 .filter(User.username == username).one()
401 .filter(User.username == username).one()
402 try:
402 try:
403 response = self.app.post(url('user', user_id=new_user.user_id),
403 response = self.app.post(url('user', user_id=new_user.user_id),
404 params={'_method': 'delete',
404 params={'_method': 'delete',
405 'user_user_groups': 'detach',
405 'user_user_groups': 'detach',
406 'csrf_token': self.csrf_token})
406 'csrf_token': self.csrf_token})
407
407
408 msg = 'Detached 1 user groups'
408 msg = 'Detached 1 user groups'
409 assert_session_flash(response, msg)
409 assert_session_flash(response, msg)
410 finally:
410 finally:
411 fixture.destroy_user_group(obj_name)
411 fixture.destroy_user_group(obj_name)
412
412
413 def test_delete_owner_of_user_group_deleting(self):
413 def test_delete_owner_of_user_group_deleting(self):
414 self.log_user()
414 self.log_user()
415 username = 'newtestuserdeleteme_user_group_owner_deleting'
415 username = 'newtestuserdeleteme_user_group_owner_deleting'
416 obj_name = 'test_user_group'
416 obj_name = 'test_user_group'
417 usr = fixture.create_user(name=username)
417 usr = fixture.create_user(name=username)
418 self.destroy_users.add(username)
418 self.destroy_users.add(username)
419 fixture.create_user_group(obj_name, cur_user=usr.username)
419 fixture.create_user_group(obj_name, cur_user=usr.username)
420
420
421 new_user = Session().query(User)\
421 new_user = Session().query(User)\
422 .filter(User.username == username).one()
422 .filter(User.username == username).one()
423 response = self.app.post(url('user', user_id=new_user.user_id),
423 response = self.app.post(url('user', user_id=new_user.user_id),
424 params={'_method': 'delete',
424 params={'_method': 'delete',
425 'user_user_groups': 'delete',
425 'user_user_groups': 'delete',
426 'csrf_token': self.csrf_token})
426 'csrf_token': self.csrf_token})
427
427
428 msg = 'Deleted 1 user groups'
428 msg = 'Deleted 1 user groups'
429 assert_session_flash(response, msg)
429 assert_session_flash(response, msg)
430
430
431 def test_edit(self):
431 def test_edit(self):
432 self.log_user()
432 self.log_user()
433 user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
433 user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
434 self.app.get(url('edit_user', user_id=user.user_id))
434 self.app.get(url('edit_user', user_id=user.user_id))
435
435
436 @pytest.mark.parametrize(
436 @pytest.mark.parametrize(
437 'repo_create, repo_create_write, user_group_create, repo_group_create,'
437 'repo_create, repo_create_write, user_group_create, repo_group_create,'
438 'fork_create, inherit_default_permissions, expect_error,'
438 'fork_create, inherit_default_permissions, expect_error,'
439 'expect_form_error', [
439 'expect_form_error', [
440 ('hg.create.none', 'hg.create.write_on_repogroup.false',
440 ('hg.create.none', 'hg.create.write_on_repogroup.false',
441 'hg.usergroup.create.false', 'hg.repogroup.create.false',
441 'hg.usergroup.create.false', 'hg.repogroup.create.false',
442 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
442 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
443 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
443 ('hg.create.repository', 'hg.create.write_on_repogroup.false',
444 'hg.usergroup.create.false', 'hg.repogroup.create.false',
444 'hg.usergroup.create.false', 'hg.repogroup.create.false',
445 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
445 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
446 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
446 ('hg.create.repository', 'hg.create.write_on_repogroup.true',
447 'hg.usergroup.create.true', 'hg.repogroup.create.true',
447 'hg.usergroup.create.true', 'hg.repogroup.create.true',
448 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
448 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
449 False),
449 False),
450 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
450 ('hg.create.XXX', 'hg.create.write_on_repogroup.true',
451 'hg.usergroup.create.true', 'hg.repogroup.create.true',
451 'hg.usergroup.create.true', 'hg.repogroup.create.true',
452 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
452 'hg.fork.repository', 'hg.inherit_default_perms.false', False,
453 True),
453 True),
454 ('', '', '', '', '', '', True, False),
454 ('', '', '', '', '', '', True, False),
455 ])
455 ])
456 def test_global_perms_on_user(
456 def test_global_perms_on_user(
457 self, repo_create, repo_create_write, user_group_create,
457 self, repo_create, repo_create_write, user_group_create,
458 repo_group_create, fork_create, expect_error, expect_form_error,
458 repo_group_create, fork_create, expect_error, expect_form_error,
459 inherit_default_permissions):
459 inherit_default_permissions):
460 self.log_user()
460 self.log_user()
461 user = fixture.create_user('dummy')
461 user = fixture.create_user('dummy')
462 uid = user.user_id
462 uid = user.user_id
463
463
464 # ENABLE REPO CREATE ON A GROUP
464 # ENABLE REPO CREATE ON A GROUP
465 perm_params = {
465 perm_params = {
466 'inherit_default_permissions': False,
466 'inherit_default_permissions': False,
467 'default_repo_create': repo_create,
467 'default_repo_create': repo_create,
468 'default_repo_create_on_write': repo_create_write,
468 'default_repo_create_on_write': repo_create_write,
469 'default_user_group_create': user_group_create,
469 'default_user_group_create': user_group_create,
470 'default_repo_group_create': repo_group_create,
470 'default_repo_group_create': repo_group_create,
471 'default_fork_create': fork_create,
471 'default_fork_create': fork_create,
472 'default_inherit_default_permissions': inherit_default_permissions,
472 'default_inherit_default_permissions': inherit_default_permissions,
473 '_method': 'put',
473 '_method': 'put',
474 'csrf_token': self.csrf_token,
474 'csrf_token': self.csrf_token,
475 }
475 }
476 response = self.app.post(
476 response = self.app.post(
477 url('edit_user_global_perms', user_id=uid),
477 url('edit_user_global_perms', user_id=uid),
478 params=perm_params)
478 params=perm_params)
479
479
480 if expect_form_error:
480 if expect_form_error:
481 assert response.status_int == 200
481 assert response.status_int == 200
482 response.mustcontain('Value must be one of')
482 response.mustcontain('Value must be one of')
483 else:
483 else:
484 if expect_error:
484 if expect_error:
485 msg = 'An error occurred during permissions saving'
485 msg = 'An error occurred during permissions saving'
486 else:
486 else:
487 msg = 'User global permissions updated successfully'
487 msg = 'User global permissions updated successfully'
488 ug = User.get(uid)
488 ug = User.get(uid)
489 del perm_params['_method']
489 del perm_params['_method']
490 del perm_params['inherit_default_permissions']
490 del perm_params['inherit_default_permissions']
491 del perm_params['csrf_token']
491 del perm_params['csrf_token']
492 assert perm_params == ug.get_default_perms()
492 assert perm_params == ug.get_default_perms()
493 assert_session_flash(response, msg)
493 assert_session_flash(response, msg)
494 fixture.destroy_user(uid)
494 fixture.destroy_user(uid)
495
495
496 def test_global_permissions_initial_values(self, user_util):
496 def test_global_permissions_initial_values(self, user_util):
497 self.log_user()
497 self.log_user()
498 user = user_util.create_user()
498 user = user_util.create_user()
499 uid = user.user_id
499 uid = user.user_id
500 response = self.app.get(url('edit_user_global_perms', user_id=uid))
500 response = self.app.get(url('edit_user_global_perms', user_id=uid))
501 default_user = User.get_default_user()
501 default_user = User.get_default_user()
502 default_permissions = default_user.get_default_perms()
502 default_permissions = default_user.get_default_perms()
503 assert_response = AssertResponse(response)
503 assert_response = AssertResponse(response)
504 expected_permissions = (
504 expected_permissions = (
505 'default_repo_create', 'default_repo_create_on_write',
505 'default_repo_create', 'default_repo_create_on_write',
506 'default_fork_create', 'default_repo_group_create',
506 'default_fork_create', 'default_repo_group_create',
507 'default_user_group_create', 'default_inherit_default_permissions')
507 'default_user_group_create', 'default_inherit_default_permissions')
508 for permission in expected_permissions:
508 for permission in expected_permissions:
509 css_selector = '[name={}][checked=checked]'.format(permission)
509 css_selector = '[name={}][checked=checked]'.format(permission)
510 element = assert_response.get_element(css_selector)
510 element = assert_response.get_element(css_selector)
511 assert element.value == default_permissions[permission]
511 assert element.value == default_permissions[permission]
512
512
513 def test_ips(self):
514 self.log_user()
515 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
516 response = self.app.get(url('edit_user_ips', user_id=user.user_id))
517 response.mustcontain('All IP addresses are allowed')
518
519 @pytest.mark.parametrize("test_name, ip, ip_range, failure", [
520 ('127/24', '127.0.0.1/24', '127.0.0.0 - 127.0.0.255', False),
521 ('10/32', '10.0.0.10/32', '10.0.0.10 - 10.0.0.10', False),
522 ('0/16', '0.0.0.0/16', '0.0.0.0 - 0.0.255.255', False),
523 ('0/8', '0.0.0.0/8', '0.0.0.0 - 0.255.255.255', False),
524 ('127_bad_mask', '127.0.0.1/99', '127.0.0.1 - 127.0.0.1', True),
525 ('127_bad_ip', 'foobar', 'foobar', True),
526 ])
527 def test_add_ip(self, test_name, ip, ip_range, failure):
528 self.log_user()
529 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
530 user_id = user.user_id
531
532 response = self.app.post(url('edit_user_ips', user_id=user_id),
533 params={'new_ip': ip, '_method': 'put',
534 'csrf_token': self.csrf_token})
535
536 if failure:
537 assert_session_flash(
538 response, 'Please enter a valid IPv4 or IpV6 address')
539 response = self.app.get(url('edit_user_ips', user_id=user_id))
540 response.mustcontain(no=[ip])
541 response.mustcontain(no=[ip_range])
542
543 else:
544 response = self.app.get(url('edit_user_ips', user_id=user_id))
545 response.mustcontain(ip)
546 response.mustcontain(ip_range)
547
548 # cleanup
549 for del_ip in UserIpMap.query().filter(
550 UserIpMap.user_id == user_id).all():
551 Session().delete(del_ip)
552 Session().commit()
553
554 def test_delete_ip(self):
555 self.log_user()
556 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
557 user_id = user.user_id
558 ip = '127.0.0.1/32'
559 ip_range = '127.0.0.1 - 127.0.0.1'
560 new_ip = UserModel().add_extra_ip(user_id, ip)
561 Session().commit()
562 new_ip_id = new_ip.ip_id
563
564 response = self.app.get(url('edit_user_ips', user_id=user_id))
565 response.mustcontain(ip)
566 response.mustcontain(ip_range)
567
568 self.app.post(url('edit_user_ips', user_id=user_id),
569 params={'_method': 'delete', 'del_ip_id': new_ip_id,
570 'csrf_token': self.csrf_token})
571
572 response = self.app.get(url('edit_user_ips', user_id=user_id))
573 response.mustcontain('All IP addresses are allowed')
574 response.mustcontain(no=[ip])
575 response.mustcontain(no=[ip_range])
General Comments 0
You need to be logged in to leave comments. Login now