##// END OF EJS Templates
user-groups: added info, and new panel to control user group synchronization....
marcink -
r1600:081c7a81 default
parent child Browse files
Show More
@@ -1,1149 +1,1153 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 from rhodecode.config import routing_links
35 from rhodecode.config import routing_links
36
36
37 # prefix for non repository related links needs to be prefixed with `/`
37 # prefix for non repository related links needs to be prefixed with `/`
38 ADMIN_PREFIX = '/_admin'
38 ADMIN_PREFIX = '/_admin'
39 STATIC_FILE_PREFIX = '/_static'
39 STATIC_FILE_PREFIX = '/_static'
40
40
41 # Default requirements for URL parts
41 # Default requirements for URL parts
42 URL_NAME_REQUIREMENTS = {
42 URL_NAME_REQUIREMENTS = {
43 # group name can have a slash in them, but they must not end with a slash
43 # group name can have a slash in them, but they must not end with a slash
44 'group_name': r'.*?[^/]',
44 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
46 # repo names can have a slash in them, but they must not end with a slash
46 # repo names can have a slash in them, but they must not end with a slash
47 'repo_name': r'.*?[^/]',
47 'repo_name': r'.*?[^/]',
48 # file path eats up everything at the end
48 # file path eats up everything at the end
49 'f_path': r'.*',
49 'f_path': r'.*',
50 # reference types
50 # reference types
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 }
53 }
54
54
55
55
56 def add_route_requirements(route_path, requirements):
56 def add_route_requirements(route_path, requirements):
57 """
57 """
58 Adds regex requirements to pyramid routes using a mapping dict
58 Adds regex requirements to pyramid routes using a mapping dict
59
59
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 '/{action}/{id:\d+}'
61 '/{action}/{id:\d+}'
62
62
63 """
63 """
64 for key, regex in requirements.items():
64 for key, regex in requirements.items():
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 return route_path
66 return route_path
67
67
68
68
69 class JSRoutesMapper(Mapper):
69 class JSRoutesMapper(Mapper):
70 """
70 """
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 """
72 """
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 def __init__(self, *args, **kw):
75 def __init__(self, *args, **kw):
76 super(JSRoutesMapper, self).__init__(*args, **kw)
76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 self._jsroutes = []
77 self._jsroutes = []
78
78
79 def connect(self, *args, **kw):
79 def connect(self, *args, **kw):
80 """
80 """
81 Wrapper for connect to take an extra argument jsroute=True
81 Wrapper for connect to take an extra argument jsroute=True
82
82
83 :param jsroute: boolean, if True will add the route to the pyroutes list
83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 """
84 """
85 if kw.pop('jsroute', False):
85 if kw.pop('jsroute', False):
86 if not self._named_route_regex.match(args[0]):
86 if not self._named_route_regex.match(args[0]):
87 raise Exception('only named routes can be added to pyroutes')
87 raise Exception('only named routes can be added to pyroutes')
88 self._jsroutes.append(args[0])
88 self._jsroutes.append(args[0])
89
89
90 super(JSRoutesMapper, self).connect(*args, **kw)
90 super(JSRoutesMapper, self).connect(*args, **kw)
91
91
92 def _extract_route_information(self, route):
92 def _extract_route_information(self, route):
93 """
93 """
94 Convert a route into tuple(name, path, args), eg:
94 Convert a route into tuple(name, path, args), eg:
95 ('show_user', '/profile/%(username)s', ['username'])
95 ('show_user', '/profile/%(username)s', ['username'])
96 """
96 """
97 routepath = route.routepath
97 routepath = route.routepath
98 def replace(matchobj):
98 def replace(matchobj):
99 if matchobj.group(1):
99 if matchobj.group(1):
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 else:
101 else:
102 return "%%(%s)s" % matchobj.group(2)
102 return "%%(%s)s" % matchobj.group(2)
103
103
104 routepath = self._argument_prog.sub(replace, routepath)
104 routepath = self._argument_prog.sub(replace, routepath)
105 return (
105 return (
106 route.name,
106 route.name,
107 routepath,
107 routepath,
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 for arg in self._argument_prog.findall(route.routepath)]
109 for arg in self._argument_prog.findall(route.routepath)]
110 )
110 )
111
111
112 def jsroutes(self):
112 def jsroutes(self):
113 """
113 """
114 Return a list of pyroutes.js compatible routes
114 Return a list of pyroutes.js compatible routes
115 """
115 """
116 for route_name in self._jsroutes:
116 for route_name in self._jsroutes:
117 yield self._extract_route_information(self._routenames[route_name])
117 yield self._extract_route_information(self._routenames[route_name])
118
118
119
119
120 def make_map(config):
120 def make_map(config):
121 """Create, configure and return the routes Mapper"""
121 """Create, configure and return the routes Mapper"""
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 always_scan=config['debug'])
123 always_scan=config['debug'])
124 rmap.minimization = False
124 rmap.minimization = False
125 rmap.explicit = False
125 rmap.explicit = False
126
126
127 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.lib.utils2 import str2bool
128 from rhodecode.model import repo, repo_group
128 from rhodecode.model import repo, repo_group
129
129
130 def check_repo(environ, match_dict):
130 def check_repo(environ, match_dict):
131 """
131 """
132 check for valid repository for proper 404 handling
132 check for valid repository for proper 404 handling
133
133
134 :param environ:
134 :param environ:
135 :param match_dict:
135 :param match_dict:
136 """
136 """
137 repo_name = match_dict.get('repo_name')
137 repo_name = match_dict.get('repo_name')
138
138
139 if match_dict.get('f_path'):
139 if match_dict.get('f_path'):
140 # fix for multiple initial slashes that causes errors
140 # fix for multiple initial slashes that causes errors
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 repo_model = repo.RepoModel()
142 repo_model = repo.RepoModel()
143 by_name_match = repo_model.get_by_repo_name(repo_name)
143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 # if we match quickly from database, short circuit the operation,
144 # if we match quickly from database, short circuit the operation,
145 # and validate repo based on the type.
145 # and validate repo based on the type.
146 if by_name_match:
146 if by_name_match:
147 return True
147 return True
148
148
149 by_id_match = repo_model.get_repo_by_id(repo_name)
149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 if by_id_match:
150 if by_id_match:
151 repo_name = by_id_match.repo_name
151 repo_name = by_id_match.repo_name
152 match_dict['repo_name'] = repo_name
152 match_dict['repo_name'] = repo_name
153 return True
153 return True
154
154
155 return False
155 return False
156
156
157 def check_group(environ, match_dict):
157 def check_group(environ, match_dict):
158 """
158 """
159 check for valid repository group path for proper 404 handling
159 check for valid repository group path for proper 404 handling
160
160
161 :param environ:
161 :param environ:
162 :param match_dict:
162 :param match_dict:
163 """
163 """
164 repo_group_name = match_dict.get('group_name')
164 repo_group_name = match_dict.get('group_name')
165 repo_group_model = repo_group.RepoGroupModel()
165 repo_group_model = repo_group.RepoGroupModel()
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 if by_name_match:
167 if by_name_match:
168 return True
168 return True
169
169
170 return False
170 return False
171
171
172 def check_user_group(environ, match_dict):
172 def check_user_group(environ, match_dict):
173 """
173 """
174 check for valid user group for proper 404 handling
174 check for valid user group for proper 404 handling
175
175
176 :param environ:
176 :param environ:
177 :param match_dict:
177 :param match_dict:
178 """
178 """
179 return True
179 return True
180
180
181 def check_int(environ, match_dict):
181 def check_int(environ, match_dict):
182 return match_dict.get('id').isdigit()
182 return match_dict.get('id').isdigit()
183
183
184
184
185 #==========================================================================
185 #==========================================================================
186 # CUSTOM ROUTES HERE
186 # CUSTOM ROUTES HERE
187 #==========================================================================
187 #==========================================================================
188
188
189 # MAIN PAGE
189 # MAIN PAGE
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 action='goto_switcher_data')
192 action='goto_switcher_data')
193 rmap.connect('repo_list_data', '/_repos', controller='home',
193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 action='repo_list_data')
194 action='repo_list_data')
195
195
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
197 action='user_autocomplete_data', jsroute=True)
197 action='user_autocomplete_data', jsroute=True)
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
199 action='user_group_autocomplete_data', jsroute=True)
199 action='user_group_autocomplete_data', jsroute=True)
200
200
201 # TODO: johbo: Static links, to be replaced by our redirection mechanism
201 # TODO: johbo: Static links, to be replaced by our redirection mechanism
202 rmap.connect('rst_help',
202 rmap.connect('rst_help',
203 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
203 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
204 _static=True)
204 _static=True)
205 rmap.connect('markdown_help',
205 rmap.connect('markdown_help',
206 'http://daringfireball.net/projects/markdown/syntax',
206 'http://daringfireball.net/projects/markdown/syntax',
207 _static=True)
207 _static=True)
208 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
208 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
209 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
209 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
210 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
210 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
211 # TODO: anderson - making this a static link since redirect won't play
211 # TODO: anderson - making this a static link since redirect won't play
212 # nice with POST requests
212 # nice with POST requests
213 rmap.connect('enterprise_license_convert_from_old',
213 rmap.connect('enterprise_license_convert_from_old',
214 'https://rhodecode.com/u/license-upgrade',
214 'https://rhodecode.com/u/license-upgrade',
215 _static=True)
215 _static=True)
216
216
217 routing_links.connect_redirection_links(rmap)
217 routing_links.connect_redirection_links(rmap)
218
218
219 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
219 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
220 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
220 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
221
221
222 # ADMIN REPOSITORY ROUTES
222 # ADMIN REPOSITORY ROUTES
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
224 controller='admin/repos') as m:
224 controller='admin/repos') as m:
225 m.connect('repos', '/repos',
225 m.connect('repos', '/repos',
226 action='create', conditions={'method': ['POST']})
226 action='create', conditions={'method': ['POST']})
227 m.connect('repos', '/repos',
227 m.connect('repos', '/repos',
228 action='index', conditions={'method': ['GET']})
228 action='index', conditions={'method': ['GET']})
229 m.connect('new_repo', '/create_repository', jsroute=True,
229 m.connect('new_repo', '/create_repository', jsroute=True,
230 action='create_repository', conditions={'method': ['GET']})
230 action='create_repository', conditions={'method': ['GET']})
231 m.connect('/repos/{repo_name}',
231 m.connect('/repos/{repo_name}',
232 action='update', conditions={'method': ['PUT'],
232 action='update', conditions={'method': ['PUT'],
233 'function': check_repo},
233 'function': check_repo},
234 requirements=URL_NAME_REQUIREMENTS)
234 requirements=URL_NAME_REQUIREMENTS)
235 m.connect('delete_repo', '/repos/{repo_name}',
235 m.connect('delete_repo', '/repos/{repo_name}',
236 action='delete', conditions={'method': ['DELETE']},
236 action='delete', conditions={'method': ['DELETE']},
237 requirements=URL_NAME_REQUIREMENTS)
237 requirements=URL_NAME_REQUIREMENTS)
238 m.connect('repo', '/repos/{repo_name}',
238 m.connect('repo', '/repos/{repo_name}',
239 action='show', conditions={'method': ['GET'],
239 action='show', conditions={'method': ['GET'],
240 'function': check_repo},
240 'function': check_repo},
241 requirements=URL_NAME_REQUIREMENTS)
241 requirements=URL_NAME_REQUIREMENTS)
242
242
243 # ADMIN REPOSITORY GROUPS ROUTES
243 # ADMIN REPOSITORY GROUPS ROUTES
244 with rmap.submapper(path_prefix=ADMIN_PREFIX,
244 with rmap.submapper(path_prefix=ADMIN_PREFIX,
245 controller='admin/repo_groups') as m:
245 controller='admin/repo_groups') as m:
246 m.connect('repo_groups', '/repo_groups',
246 m.connect('repo_groups', '/repo_groups',
247 action='create', conditions={'method': ['POST']})
247 action='create', conditions={'method': ['POST']})
248 m.connect('repo_groups', '/repo_groups',
248 m.connect('repo_groups', '/repo_groups',
249 action='index', conditions={'method': ['GET']})
249 action='index', conditions={'method': ['GET']})
250 m.connect('new_repo_group', '/repo_groups/new',
250 m.connect('new_repo_group', '/repo_groups/new',
251 action='new', conditions={'method': ['GET']})
251 action='new', conditions={'method': ['GET']})
252 m.connect('update_repo_group', '/repo_groups/{group_name}',
252 m.connect('update_repo_group', '/repo_groups/{group_name}',
253 action='update', conditions={'method': ['PUT'],
253 action='update', conditions={'method': ['PUT'],
254 'function': check_group},
254 'function': check_group},
255 requirements=URL_NAME_REQUIREMENTS)
255 requirements=URL_NAME_REQUIREMENTS)
256
256
257 # EXTRAS REPO GROUP ROUTES
257 # EXTRAS REPO GROUP ROUTES
258 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
258 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
259 action='edit',
259 action='edit',
260 conditions={'method': ['GET'], 'function': check_group},
260 conditions={'method': ['GET'], 'function': check_group},
261 requirements=URL_NAME_REQUIREMENTS)
261 requirements=URL_NAME_REQUIREMENTS)
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
263 action='edit',
263 action='edit',
264 conditions={'method': ['PUT'], 'function': check_group},
264 conditions={'method': ['PUT'], 'function': check_group},
265 requirements=URL_NAME_REQUIREMENTS)
265 requirements=URL_NAME_REQUIREMENTS)
266
266
267 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
267 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
268 action='edit_repo_group_advanced',
268 action='edit_repo_group_advanced',
269 conditions={'method': ['GET'], 'function': check_group},
269 conditions={'method': ['GET'], 'function': check_group},
270 requirements=URL_NAME_REQUIREMENTS)
270 requirements=URL_NAME_REQUIREMENTS)
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
272 action='edit_repo_group_advanced',
272 action='edit_repo_group_advanced',
273 conditions={'method': ['PUT'], 'function': check_group},
273 conditions={'method': ['PUT'], 'function': check_group},
274 requirements=URL_NAME_REQUIREMENTS)
274 requirements=URL_NAME_REQUIREMENTS)
275
275
276 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
276 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
277 action='edit_repo_group_perms',
277 action='edit_repo_group_perms',
278 conditions={'method': ['GET'], 'function': check_group},
278 conditions={'method': ['GET'], 'function': check_group},
279 requirements=URL_NAME_REQUIREMENTS)
279 requirements=URL_NAME_REQUIREMENTS)
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
281 action='update_perms',
281 action='update_perms',
282 conditions={'method': ['PUT'], 'function': check_group},
282 conditions={'method': ['PUT'], 'function': check_group},
283 requirements=URL_NAME_REQUIREMENTS)
283 requirements=URL_NAME_REQUIREMENTS)
284
284
285 m.connect('delete_repo_group', '/repo_groups/{group_name}',
285 m.connect('delete_repo_group', '/repo_groups/{group_name}',
286 action='delete', conditions={'method': ['DELETE'],
286 action='delete', conditions={'method': ['DELETE'],
287 'function': check_group},
287 'function': check_group},
288 requirements=URL_NAME_REQUIREMENTS)
288 requirements=URL_NAME_REQUIREMENTS)
289
289
290 # ADMIN USER ROUTES
290 # ADMIN USER ROUTES
291 with rmap.submapper(path_prefix=ADMIN_PREFIX,
291 with rmap.submapper(path_prefix=ADMIN_PREFIX,
292 controller='admin/users') as m:
292 controller='admin/users') as m:
293 m.connect('users', '/users',
293 m.connect('users', '/users',
294 action='create', conditions={'method': ['POST']})
294 action='create', conditions={'method': ['POST']})
295 m.connect('new_user', '/users/new',
295 m.connect('new_user', '/users/new',
296 action='new', conditions={'method': ['GET']})
296 action='new', conditions={'method': ['GET']})
297 m.connect('update_user', '/users/{user_id}',
297 m.connect('update_user', '/users/{user_id}',
298 action='update', conditions={'method': ['PUT']})
298 action='update', conditions={'method': ['PUT']})
299 m.connect('delete_user', '/users/{user_id}',
299 m.connect('delete_user', '/users/{user_id}',
300 action='delete', conditions={'method': ['DELETE']})
300 action='delete', conditions={'method': ['DELETE']})
301 m.connect('edit_user', '/users/{user_id}/edit',
301 m.connect('edit_user', '/users/{user_id}/edit',
302 action='edit', conditions={'method': ['GET']}, jsroute=True)
302 action='edit', conditions={'method': ['GET']}, jsroute=True)
303 m.connect('user', '/users/{user_id}',
303 m.connect('user', '/users/{user_id}',
304 action='show', conditions={'method': ['GET']})
304 action='show', conditions={'method': ['GET']})
305 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
305 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
306 action='reset_password', conditions={'method': ['POST']})
306 action='reset_password', conditions={'method': ['POST']})
307 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
307 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
308 action='create_personal_repo_group', conditions={'method': ['POST']})
308 action='create_personal_repo_group', conditions={'method': ['POST']})
309
309
310 # EXTRAS USER ROUTES
310 # EXTRAS USER ROUTES
311 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
311 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
312 action='edit_advanced', conditions={'method': ['GET']})
312 action='edit_advanced', conditions={'method': ['GET']})
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
313 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
314 action='update_advanced', conditions={'method': ['PUT']})
314 action='update_advanced', conditions={'method': ['PUT']})
315
315
316 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
316 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
317 action='edit_global_perms', conditions={'method': ['GET']})
317 action='edit_global_perms', conditions={'method': ['GET']})
318 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
318 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
319 action='update_global_perms', conditions={'method': ['PUT']})
319 action='update_global_perms', conditions={'method': ['PUT']})
320
320
321 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
321 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
322 action='edit_perms_summary', conditions={'method': ['GET']})
322 action='edit_perms_summary', conditions={'method': ['GET']})
323
323
324 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
324 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
325 action='edit_emails', conditions={'method': ['GET']})
325 action='edit_emails', conditions={'method': ['GET']})
326 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
326 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
327 action='add_email', conditions={'method': ['PUT']})
327 action='add_email', conditions={'method': ['PUT']})
328 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
328 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
329 action='delete_email', conditions={'method': ['DELETE']})
329 action='delete_email', conditions={'method': ['DELETE']})
330
330
331 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
331 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
332 action='edit_ips', conditions={'method': ['GET']})
332 action='edit_ips', conditions={'method': ['GET']})
333 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
333 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
334 action='add_ip', conditions={'method': ['PUT']})
334 action='add_ip', conditions={'method': ['PUT']})
335 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
335 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
336 action='delete_ip', conditions={'method': ['DELETE']})
336 action='delete_ip', conditions={'method': ['DELETE']})
337
337
338 # ADMIN USER GROUPS REST ROUTES
338 # ADMIN USER GROUPS REST ROUTES
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
340 controller='admin/user_groups') as m:
340 controller='admin/user_groups') as m:
341 m.connect('users_groups', '/user_groups',
341 m.connect('users_groups', '/user_groups',
342 action='create', conditions={'method': ['POST']})
342 action='create', conditions={'method': ['POST']})
343 m.connect('users_groups', '/user_groups',
343 m.connect('users_groups', '/user_groups',
344 action='index', conditions={'method': ['GET']})
344 action='index', conditions={'method': ['GET']})
345 m.connect('new_users_group', '/user_groups/new',
345 m.connect('new_users_group', '/user_groups/new',
346 action='new', conditions={'method': ['GET']})
346 action='new', conditions={'method': ['GET']})
347 m.connect('update_users_group', '/user_groups/{user_group_id}',
347 m.connect('update_users_group', '/user_groups/{user_group_id}',
348 action='update', conditions={'method': ['PUT']})
348 action='update', conditions={'method': ['PUT']})
349 m.connect('delete_users_group', '/user_groups/{user_group_id}',
349 m.connect('delete_users_group', '/user_groups/{user_group_id}',
350 action='delete', conditions={'method': ['DELETE']})
350 action='delete', conditions={'method': ['DELETE']})
351 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
351 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
352 action='edit', conditions={'method': ['GET']},
352 action='edit', conditions={'method': ['GET']},
353 function=check_user_group)
353 function=check_user_group)
354
354
355 # EXTRAS USER GROUP ROUTES
355 # EXTRAS USER GROUP ROUTES
356 m.connect('edit_user_group_global_perms',
356 m.connect('edit_user_group_global_perms',
357 '/user_groups/{user_group_id}/edit/global_permissions',
357 '/user_groups/{user_group_id}/edit/global_permissions',
358 action='edit_global_perms', conditions={'method': ['GET']})
358 action='edit_global_perms', conditions={'method': ['GET']})
359 m.connect('edit_user_group_global_perms',
359 m.connect('edit_user_group_global_perms',
360 '/user_groups/{user_group_id}/edit/global_permissions',
360 '/user_groups/{user_group_id}/edit/global_permissions',
361 action='update_global_perms', conditions={'method': ['PUT']})
361 action='update_global_perms', conditions={'method': ['PUT']})
362 m.connect('edit_user_group_perms_summary',
362 m.connect('edit_user_group_perms_summary',
363 '/user_groups/{user_group_id}/edit/permissions_summary',
363 '/user_groups/{user_group_id}/edit/permissions_summary',
364 action='edit_perms_summary', conditions={'method': ['GET']})
364 action='edit_perms_summary', conditions={'method': ['GET']})
365
365
366 m.connect('edit_user_group_perms',
366 m.connect('edit_user_group_perms',
367 '/user_groups/{user_group_id}/edit/permissions',
367 '/user_groups/{user_group_id}/edit/permissions',
368 action='edit_perms', conditions={'method': ['GET']})
368 action='edit_perms', conditions={'method': ['GET']})
369 m.connect('edit_user_group_perms',
369 m.connect('edit_user_group_perms',
370 '/user_groups/{user_group_id}/edit/permissions',
370 '/user_groups/{user_group_id}/edit/permissions',
371 action='update_perms', conditions={'method': ['PUT']})
371 action='update_perms', conditions={'method': ['PUT']})
372
372
373 m.connect('edit_user_group_advanced',
373 m.connect('edit_user_group_advanced',
374 '/user_groups/{user_group_id}/edit/advanced',
374 '/user_groups/{user_group_id}/edit/advanced',
375 action='edit_advanced', conditions={'method': ['GET']})
375 action='edit_advanced', conditions={'method': ['GET']})
376
376
377 m.connect('edit_user_group_advanced_sync',
378 '/user_groups/{user_group_id}/edit/advanced/sync',
379 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
380
377 m.connect('edit_user_group_members',
381 m.connect('edit_user_group_members',
378 '/user_groups/{user_group_id}/edit/members', jsroute=True,
382 '/user_groups/{user_group_id}/edit/members', jsroute=True,
379 action='user_group_members', conditions={'method': ['GET']})
383 action='user_group_members', conditions={'method': ['GET']})
380
384
381 # ADMIN PERMISSIONS ROUTES
385 # ADMIN PERMISSIONS ROUTES
382 with rmap.submapper(path_prefix=ADMIN_PREFIX,
386 with rmap.submapper(path_prefix=ADMIN_PREFIX,
383 controller='admin/permissions') as m:
387 controller='admin/permissions') as m:
384 m.connect('admin_permissions_application', '/permissions/application',
388 m.connect('admin_permissions_application', '/permissions/application',
385 action='permission_application_update', conditions={'method': ['POST']})
389 action='permission_application_update', conditions={'method': ['POST']})
386 m.connect('admin_permissions_application', '/permissions/application',
390 m.connect('admin_permissions_application', '/permissions/application',
387 action='permission_application', conditions={'method': ['GET']})
391 action='permission_application', conditions={'method': ['GET']})
388
392
389 m.connect('admin_permissions_global', '/permissions/global',
393 m.connect('admin_permissions_global', '/permissions/global',
390 action='permission_global_update', conditions={'method': ['POST']})
394 action='permission_global_update', conditions={'method': ['POST']})
391 m.connect('admin_permissions_global', '/permissions/global',
395 m.connect('admin_permissions_global', '/permissions/global',
392 action='permission_global', conditions={'method': ['GET']})
396 action='permission_global', conditions={'method': ['GET']})
393
397
394 m.connect('admin_permissions_object', '/permissions/object',
398 m.connect('admin_permissions_object', '/permissions/object',
395 action='permission_objects_update', conditions={'method': ['POST']})
399 action='permission_objects_update', conditions={'method': ['POST']})
396 m.connect('admin_permissions_object', '/permissions/object',
400 m.connect('admin_permissions_object', '/permissions/object',
397 action='permission_objects', conditions={'method': ['GET']})
401 action='permission_objects', conditions={'method': ['GET']})
398
402
399 m.connect('admin_permissions_ips', '/permissions/ips',
403 m.connect('admin_permissions_ips', '/permissions/ips',
400 action='permission_ips', conditions={'method': ['POST']})
404 action='permission_ips', conditions={'method': ['POST']})
401 m.connect('admin_permissions_ips', '/permissions/ips',
405 m.connect('admin_permissions_ips', '/permissions/ips',
402 action='permission_ips', conditions={'method': ['GET']})
406 action='permission_ips', conditions={'method': ['GET']})
403
407
404 m.connect('admin_permissions_overview', '/permissions/overview',
408 m.connect('admin_permissions_overview', '/permissions/overview',
405 action='permission_perms', conditions={'method': ['GET']})
409 action='permission_perms', conditions={'method': ['GET']})
406
410
407 # ADMIN DEFAULTS REST ROUTES
411 # ADMIN DEFAULTS REST ROUTES
408 with rmap.submapper(path_prefix=ADMIN_PREFIX,
412 with rmap.submapper(path_prefix=ADMIN_PREFIX,
409 controller='admin/defaults') as m:
413 controller='admin/defaults') as m:
410 m.connect('admin_defaults_repositories', '/defaults/repositories',
414 m.connect('admin_defaults_repositories', '/defaults/repositories',
411 action='update_repository_defaults', conditions={'method': ['POST']})
415 action='update_repository_defaults', conditions={'method': ['POST']})
412 m.connect('admin_defaults_repositories', '/defaults/repositories',
416 m.connect('admin_defaults_repositories', '/defaults/repositories',
413 action='index', conditions={'method': ['GET']})
417 action='index', conditions={'method': ['GET']})
414
418
415 # ADMIN DEBUG STYLE ROUTES
419 # ADMIN DEBUG STYLE ROUTES
416 if str2bool(config.get('debug_style')):
420 if str2bool(config.get('debug_style')):
417 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
421 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
418 controller='debug_style') as m:
422 controller='debug_style') as m:
419 m.connect('debug_style_home', '',
423 m.connect('debug_style_home', '',
420 action='index', conditions={'method': ['GET']})
424 action='index', conditions={'method': ['GET']})
421 m.connect('debug_style_template', '/t/{t_path}',
425 m.connect('debug_style_template', '/t/{t_path}',
422 action='template', conditions={'method': ['GET']})
426 action='template', conditions={'method': ['GET']})
423
427
424 # ADMIN SETTINGS ROUTES
428 # ADMIN SETTINGS ROUTES
425 with rmap.submapper(path_prefix=ADMIN_PREFIX,
429 with rmap.submapper(path_prefix=ADMIN_PREFIX,
426 controller='admin/settings') as m:
430 controller='admin/settings') as m:
427
431
428 # default
432 # default
429 m.connect('admin_settings', '/settings',
433 m.connect('admin_settings', '/settings',
430 action='settings_global_update',
434 action='settings_global_update',
431 conditions={'method': ['POST']})
435 conditions={'method': ['POST']})
432 m.connect('admin_settings', '/settings',
436 m.connect('admin_settings', '/settings',
433 action='settings_global', conditions={'method': ['GET']})
437 action='settings_global', conditions={'method': ['GET']})
434
438
435 m.connect('admin_settings_vcs', '/settings/vcs',
439 m.connect('admin_settings_vcs', '/settings/vcs',
436 action='settings_vcs_update',
440 action='settings_vcs_update',
437 conditions={'method': ['POST']})
441 conditions={'method': ['POST']})
438 m.connect('admin_settings_vcs', '/settings/vcs',
442 m.connect('admin_settings_vcs', '/settings/vcs',
439 action='settings_vcs',
443 action='settings_vcs',
440 conditions={'method': ['GET']})
444 conditions={'method': ['GET']})
441 m.connect('admin_settings_vcs', '/settings/vcs',
445 m.connect('admin_settings_vcs', '/settings/vcs',
442 action='delete_svn_pattern',
446 action='delete_svn_pattern',
443 conditions={'method': ['DELETE']})
447 conditions={'method': ['DELETE']})
444
448
445 m.connect('admin_settings_mapping', '/settings/mapping',
449 m.connect('admin_settings_mapping', '/settings/mapping',
446 action='settings_mapping_update',
450 action='settings_mapping_update',
447 conditions={'method': ['POST']})
451 conditions={'method': ['POST']})
448 m.connect('admin_settings_mapping', '/settings/mapping',
452 m.connect('admin_settings_mapping', '/settings/mapping',
449 action='settings_mapping', conditions={'method': ['GET']})
453 action='settings_mapping', conditions={'method': ['GET']})
450
454
451 m.connect('admin_settings_global', '/settings/global',
455 m.connect('admin_settings_global', '/settings/global',
452 action='settings_global_update',
456 action='settings_global_update',
453 conditions={'method': ['POST']})
457 conditions={'method': ['POST']})
454 m.connect('admin_settings_global', '/settings/global',
458 m.connect('admin_settings_global', '/settings/global',
455 action='settings_global', conditions={'method': ['GET']})
459 action='settings_global', conditions={'method': ['GET']})
456
460
457 m.connect('admin_settings_visual', '/settings/visual',
461 m.connect('admin_settings_visual', '/settings/visual',
458 action='settings_visual_update',
462 action='settings_visual_update',
459 conditions={'method': ['POST']})
463 conditions={'method': ['POST']})
460 m.connect('admin_settings_visual', '/settings/visual',
464 m.connect('admin_settings_visual', '/settings/visual',
461 action='settings_visual', conditions={'method': ['GET']})
465 action='settings_visual', conditions={'method': ['GET']})
462
466
463 m.connect('admin_settings_issuetracker',
467 m.connect('admin_settings_issuetracker',
464 '/settings/issue-tracker', action='settings_issuetracker',
468 '/settings/issue-tracker', action='settings_issuetracker',
465 conditions={'method': ['GET']})
469 conditions={'method': ['GET']})
466 m.connect('admin_settings_issuetracker_save',
470 m.connect('admin_settings_issuetracker_save',
467 '/settings/issue-tracker/save',
471 '/settings/issue-tracker/save',
468 action='settings_issuetracker_save',
472 action='settings_issuetracker_save',
469 conditions={'method': ['POST']})
473 conditions={'method': ['POST']})
470 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
474 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
471 action='settings_issuetracker_test',
475 action='settings_issuetracker_test',
472 conditions={'method': ['POST']})
476 conditions={'method': ['POST']})
473 m.connect('admin_issuetracker_delete',
477 m.connect('admin_issuetracker_delete',
474 '/settings/issue-tracker/delete',
478 '/settings/issue-tracker/delete',
475 action='settings_issuetracker_delete',
479 action='settings_issuetracker_delete',
476 conditions={'method': ['DELETE']})
480 conditions={'method': ['DELETE']})
477
481
478 m.connect('admin_settings_email', '/settings/email',
482 m.connect('admin_settings_email', '/settings/email',
479 action='settings_email_update',
483 action='settings_email_update',
480 conditions={'method': ['POST']})
484 conditions={'method': ['POST']})
481 m.connect('admin_settings_email', '/settings/email',
485 m.connect('admin_settings_email', '/settings/email',
482 action='settings_email', conditions={'method': ['GET']})
486 action='settings_email', conditions={'method': ['GET']})
483
487
484 m.connect('admin_settings_hooks', '/settings/hooks',
488 m.connect('admin_settings_hooks', '/settings/hooks',
485 action='settings_hooks_update',
489 action='settings_hooks_update',
486 conditions={'method': ['POST', 'DELETE']})
490 conditions={'method': ['POST', 'DELETE']})
487 m.connect('admin_settings_hooks', '/settings/hooks',
491 m.connect('admin_settings_hooks', '/settings/hooks',
488 action='settings_hooks', conditions={'method': ['GET']})
492 action='settings_hooks', conditions={'method': ['GET']})
489
493
490 m.connect('admin_settings_search', '/settings/search',
494 m.connect('admin_settings_search', '/settings/search',
491 action='settings_search', conditions={'method': ['GET']})
495 action='settings_search', conditions={'method': ['GET']})
492
496
493 m.connect('admin_settings_supervisor', '/settings/supervisor',
497 m.connect('admin_settings_supervisor', '/settings/supervisor',
494 action='settings_supervisor', conditions={'method': ['GET']})
498 action='settings_supervisor', conditions={'method': ['GET']})
495 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
499 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
496 action='settings_supervisor_log', conditions={'method': ['GET']})
500 action='settings_supervisor_log', conditions={'method': ['GET']})
497
501
498 m.connect('admin_settings_labs', '/settings/labs',
502 m.connect('admin_settings_labs', '/settings/labs',
499 action='settings_labs_update',
503 action='settings_labs_update',
500 conditions={'method': ['POST']})
504 conditions={'method': ['POST']})
501 m.connect('admin_settings_labs', '/settings/labs',
505 m.connect('admin_settings_labs', '/settings/labs',
502 action='settings_labs', conditions={'method': ['GET']})
506 action='settings_labs', conditions={'method': ['GET']})
503
507
504 # ADMIN MY ACCOUNT
508 # ADMIN MY ACCOUNT
505 with rmap.submapper(path_prefix=ADMIN_PREFIX,
509 with rmap.submapper(path_prefix=ADMIN_PREFIX,
506 controller='admin/my_account') as m:
510 controller='admin/my_account') as m:
507
511
508 m.connect('my_account_edit', '/my_account/edit',
512 m.connect('my_account_edit', '/my_account/edit',
509 action='my_account_edit', conditions={'method': ['GET']})
513 action='my_account_edit', conditions={'method': ['GET']})
510 m.connect('my_account', '/my_account/update',
514 m.connect('my_account', '/my_account/update',
511 action='my_account_update', conditions={'method': ['POST']})
515 action='my_account_update', conditions={'method': ['POST']})
512
516
513 # NOTE(marcink): this needs to be kept for password force flag to be
517 # NOTE(marcink): this needs to be kept for password force flag to be
514 # handler, remove after migration to pyramid
518 # handler, remove after migration to pyramid
515 m.connect('my_account_password', '/my_account/password',
519 m.connect('my_account_password', '/my_account/password',
516 action='my_account_password', conditions={'method': ['GET']})
520 action='my_account_password', conditions={'method': ['GET']})
517
521
518 m.connect('my_account_repos', '/my_account/repos',
522 m.connect('my_account_repos', '/my_account/repos',
519 action='my_account_repos', conditions={'method': ['GET']})
523 action='my_account_repos', conditions={'method': ['GET']})
520
524
521 m.connect('my_account_watched', '/my_account/watched',
525 m.connect('my_account_watched', '/my_account/watched',
522 action='my_account_watched', conditions={'method': ['GET']})
526 action='my_account_watched', conditions={'method': ['GET']})
523
527
524 m.connect('my_account_pullrequests', '/my_account/pull_requests',
528 m.connect('my_account_pullrequests', '/my_account/pull_requests',
525 action='my_account_pullrequests', conditions={'method': ['GET']})
529 action='my_account_pullrequests', conditions={'method': ['GET']})
526
530
527 m.connect('my_account_perms', '/my_account/perms',
531 m.connect('my_account_perms', '/my_account/perms',
528 action='my_account_perms', conditions={'method': ['GET']})
532 action='my_account_perms', conditions={'method': ['GET']})
529
533
530 m.connect('my_account_emails', '/my_account/emails',
534 m.connect('my_account_emails', '/my_account/emails',
531 action='my_account_emails', conditions={'method': ['GET']})
535 action='my_account_emails', conditions={'method': ['GET']})
532 m.connect('my_account_emails', '/my_account/emails',
536 m.connect('my_account_emails', '/my_account/emails',
533 action='my_account_emails_add', conditions={'method': ['POST']})
537 action='my_account_emails_add', conditions={'method': ['POST']})
534 m.connect('my_account_emails', '/my_account/emails',
538 m.connect('my_account_emails', '/my_account/emails',
535 action='my_account_emails_delete', conditions={'method': ['DELETE']})
539 action='my_account_emails_delete', conditions={'method': ['DELETE']})
536
540
537 m.connect('my_account_notifications', '/my_account/notifications',
541 m.connect('my_account_notifications', '/my_account/notifications',
538 action='my_notifications',
542 action='my_notifications',
539 conditions={'method': ['GET']})
543 conditions={'method': ['GET']})
540 m.connect('my_account_notifications_toggle_visibility',
544 m.connect('my_account_notifications_toggle_visibility',
541 '/my_account/toggle_visibility',
545 '/my_account/toggle_visibility',
542 action='my_notifications_toggle_visibility',
546 action='my_notifications_toggle_visibility',
543 conditions={'method': ['POST']})
547 conditions={'method': ['POST']})
544 m.connect('my_account_notifications_test_channelstream',
548 m.connect('my_account_notifications_test_channelstream',
545 '/my_account/test_channelstream',
549 '/my_account/test_channelstream',
546 action='my_account_notifications_test_channelstream',
550 action='my_account_notifications_test_channelstream',
547 conditions={'method': ['POST']})
551 conditions={'method': ['POST']})
548
552
549 # NOTIFICATION REST ROUTES
553 # NOTIFICATION REST ROUTES
550 with rmap.submapper(path_prefix=ADMIN_PREFIX,
554 with rmap.submapper(path_prefix=ADMIN_PREFIX,
551 controller='admin/notifications') as m:
555 controller='admin/notifications') as m:
552 m.connect('notifications', '/notifications',
556 m.connect('notifications', '/notifications',
553 action='index', conditions={'method': ['GET']})
557 action='index', conditions={'method': ['GET']})
554 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
558 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
555 action='mark_all_read', conditions={'method': ['POST']})
559 action='mark_all_read', conditions={'method': ['POST']})
556 m.connect('/notifications/{notification_id}',
560 m.connect('/notifications/{notification_id}',
557 action='update', conditions={'method': ['PUT']})
561 action='update', conditions={'method': ['PUT']})
558 m.connect('/notifications/{notification_id}',
562 m.connect('/notifications/{notification_id}',
559 action='delete', conditions={'method': ['DELETE']})
563 action='delete', conditions={'method': ['DELETE']})
560 m.connect('notification', '/notifications/{notification_id}',
564 m.connect('notification', '/notifications/{notification_id}',
561 action='show', conditions={'method': ['GET']})
565 action='show', conditions={'method': ['GET']})
562
566
563 # ADMIN GIST
567 # ADMIN GIST
564 with rmap.submapper(path_prefix=ADMIN_PREFIX,
568 with rmap.submapper(path_prefix=ADMIN_PREFIX,
565 controller='admin/gists') as m:
569 controller='admin/gists') as m:
566 m.connect('gists', '/gists',
570 m.connect('gists', '/gists',
567 action='create', conditions={'method': ['POST']})
571 action='create', conditions={'method': ['POST']})
568 m.connect('gists', '/gists', jsroute=True,
572 m.connect('gists', '/gists', jsroute=True,
569 action='index', conditions={'method': ['GET']})
573 action='index', conditions={'method': ['GET']})
570 m.connect('new_gist', '/gists/new', jsroute=True,
574 m.connect('new_gist', '/gists/new', jsroute=True,
571 action='new', conditions={'method': ['GET']})
575 action='new', conditions={'method': ['GET']})
572
576
573 m.connect('/gists/{gist_id}',
577 m.connect('/gists/{gist_id}',
574 action='delete', conditions={'method': ['DELETE']})
578 action='delete', conditions={'method': ['DELETE']})
575 m.connect('edit_gist', '/gists/{gist_id}/edit',
579 m.connect('edit_gist', '/gists/{gist_id}/edit',
576 action='edit_form', conditions={'method': ['GET']})
580 action='edit_form', conditions={'method': ['GET']})
577 m.connect('edit_gist', '/gists/{gist_id}/edit',
581 m.connect('edit_gist', '/gists/{gist_id}/edit',
578 action='edit', conditions={'method': ['POST']})
582 action='edit', conditions={'method': ['POST']})
579 m.connect(
583 m.connect(
580 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
584 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
581 action='check_revision', conditions={'method': ['GET']})
585 action='check_revision', conditions={'method': ['GET']})
582
586
583 m.connect('gist', '/gists/{gist_id}',
587 m.connect('gist', '/gists/{gist_id}',
584 action='show', conditions={'method': ['GET']})
588 action='show', conditions={'method': ['GET']})
585 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
589 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
586 revision='tip',
590 revision='tip',
587 action='show', conditions={'method': ['GET']})
591 action='show', conditions={'method': ['GET']})
588 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
592 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
589 revision='tip',
593 revision='tip',
590 action='show', conditions={'method': ['GET']})
594 action='show', conditions={'method': ['GET']})
591 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
595 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
592 revision='tip',
596 revision='tip',
593 action='show', conditions={'method': ['GET']},
597 action='show', conditions={'method': ['GET']},
594 requirements=URL_NAME_REQUIREMENTS)
598 requirements=URL_NAME_REQUIREMENTS)
595
599
596 # ADMIN MAIN PAGES
600 # ADMIN MAIN PAGES
597 with rmap.submapper(path_prefix=ADMIN_PREFIX,
601 with rmap.submapper(path_prefix=ADMIN_PREFIX,
598 controller='admin/admin') as m:
602 controller='admin/admin') as m:
599 m.connect('admin_home', '', action='index')
603 m.connect('admin_home', '', action='index')
600 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
604 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
601 action='add_repo')
605 action='add_repo')
602 m.connect(
606 m.connect(
603 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
607 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
604 action='pull_requests')
608 action='pull_requests')
605 m.connect(
609 m.connect(
606 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
610 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
607 action='pull_requests')
611 action='pull_requests')
608 m.connect(
612 m.connect(
609 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
613 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
610 action='pull_requests')
614 action='pull_requests')
611
615
612 # USER JOURNAL
616 # USER JOURNAL
613 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
617 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
614 controller='journal', action='index')
618 controller='journal', action='index')
615 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
619 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
616 controller='journal', action='journal_rss')
620 controller='journal', action='journal_rss')
617 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
621 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
618 controller='journal', action='journal_atom')
622 controller='journal', action='journal_atom')
619
623
620 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
624 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
621 controller='journal', action='public_journal')
625 controller='journal', action='public_journal')
622
626
623 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
627 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
624 controller='journal', action='public_journal_rss')
628 controller='journal', action='public_journal_rss')
625
629
626 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
630 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
627 controller='journal', action='public_journal_rss')
631 controller='journal', action='public_journal_rss')
628
632
629 rmap.connect('public_journal_atom',
633 rmap.connect('public_journal_atom',
630 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
634 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
631 action='public_journal_atom')
635 action='public_journal_atom')
632
636
633 rmap.connect('public_journal_atom_old',
637 rmap.connect('public_journal_atom_old',
634 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
638 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
635 action='public_journal_atom')
639 action='public_journal_atom')
636
640
637 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
641 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
638 controller='journal', action='toggle_following', jsroute=True,
642 controller='journal', action='toggle_following', jsroute=True,
639 conditions={'method': ['POST']})
643 conditions={'method': ['POST']})
640
644
641 # FULL TEXT SEARCH
645 # FULL TEXT SEARCH
642 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
646 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
643 controller='search')
647 controller='search')
644 rmap.connect('search_repo_home', '/{repo_name}/search',
648 rmap.connect('search_repo_home', '/{repo_name}/search',
645 controller='search',
649 controller='search',
646 action='index',
650 action='index',
647 conditions={'function': check_repo},
651 conditions={'function': check_repo},
648 requirements=URL_NAME_REQUIREMENTS)
652 requirements=URL_NAME_REQUIREMENTS)
649
653
650 # FEEDS
654 # FEEDS
651 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
655 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
652 controller='feed', action='rss',
656 controller='feed', action='rss',
653 conditions={'function': check_repo},
657 conditions={'function': check_repo},
654 requirements=URL_NAME_REQUIREMENTS)
658 requirements=URL_NAME_REQUIREMENTS)
655
659
656 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
660 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
657 controller='feed', action='atom',
661 controller='feed', action='atom',
658 conditions={'function': check_repo},
662 conditions={'function': check_repo},
659 requirements=URL_NAME_REQUIREMENTS)
663 requirements=URL_NAME_REQUIREMENTS)
660
664
661 #==========================================================================
665 #==========================================================================
662 # REPOSITORY ROUTES
666 # REPOSITORY ROUTES
663 #==========================================================================
667 #==========================================================================
664
668
665 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
669 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
666 controller='admin/repos', action='repo_creating',
670 controller='admin/repos', action='repo_creating',
667 requirements=URL_NAME_REQUIREMENTS)
671 requirements=URL_NAME_REQUIREMENTS)
668 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
672 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
669 controller='admin/repos', action='repo_check',
673 controller='admin/repos', action='repo_check',
670 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
671
675
672 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
676 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
673 controller='summary', action='repo_stats',
677 controller='summary', action='repo_stats',
674 conditions={'function': check_repo},
678 conditions={'function': check_repo},
675 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
679 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
676
680
677 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
681 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
678 controller='summary', action='repo_refs_data',
682 controller='summary', action='repo_refs_data',
679 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
683 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
680 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
684 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
681 controller='summary', action='repo_refs_changelog_data',
685 controller='summary', action='repo_refs_changelog_data',
682 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
686 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
683 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
687 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
684 controller='summary', action='repo_default_reviewers_data',
688 controller='summary', action='repo_default_reviewers_data',
685 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
689 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
686
690
687 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
691 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
688 controller='changeset', revision='tip',
692 controller='changeset', revision='tip',
689 conditions={'function': check_repo},
693 conditions={'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
694 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
691 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
695 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
692 controller='changeset', revision='tip', action='changeset_children',
696 controller='changeset', revision='tip', action='changeset_children',
693 conditions={'function': check_repo},
697 conditions={'function': check_repo},
694 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
695 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
699 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
696 controller='changeset', revision='tip', action='changeset_parents',
700 controller='changeset', revision='tip', action='changeset_parents',
697 conditions={'function': check_repo},
701 conditions={'function': check_repo},
698 requirements=URL_NAME_REQUIREMENTS)
702 requirements=URL_NAME_REQUIREMENTS)
699
703
700 # repo edit options
704 # repo edit options
701 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
705 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
702 controller='admin/repos', action='edit',
706 controller='admin/repos', action='edit',
703 conditions={'method': ['GET'], 'function': check_repo},
707 conditions={'method': ['GET'], 'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
708 requirements=URL_NAME_REQUIREMENTS)
705
709
706 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
710 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
707 jsroute=True,
711 jsroute=True,
708 controller='admin/repos', action='edit_permissions',
712 controller='admin/repos', action='edit_permissions',
709 conditions={'method': ['GET'], 'function': check_repo},
713 conditions={'method': ['GET'], 'function': check_repo},
710 requirements=URL_NAME_REQUIREMENTS)
714 requirements=URL_NAME_REQUIREMENTS)
711 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
715 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
712 controller='admin/repos', action='edit_permissions_update',
716 controller='admin/repos', action='edit_permissions_update',
713 conditions={'method': ['PUT'], 'function': check_repo},
717 conditions={'method': ['PUT'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
718 requirements=URL_NAME_REQUIREMENTS)
715
719
716 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
720 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
717 controller='admin/repos', action='edit_fields',
721 controller='admin/repos', action='edit_fields',
718 conditions={'method': ['GET'], 'function': check_repo},
722 conditions={'method': ['GET'], 'function': check_repo},
719 requirements=URL_NAME_REQUIREMENTS)
723 requirements=URL_NAME_REQUIREMENTS)
720 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
724 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
721 controller='admin/repos', action='create_repo_field',
725 controller='admin/repos', action='create_repo_field',
722 conditions={'method': ['PUT'], 'function': check_repo},
726 conditions={'method': ['PUT'], 'function': check_repo},
723 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
724 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
728 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
725 controller='admin/repos', action='delete_repo_field',
729 controller='admin/repos', action='delete_repo_field',
726 conditions={'method': ['DELETE'], 'function': check_repo},
730 conditions={'method': ['DELETE'], 'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
731 requirements=URL_NAME_REQUIREMENTS)
728
732
729 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
733 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
730 controller='admin/repos', action='edit_advanced',
734 controller='admin/repos', action='edit_advanced',
731 conditions={'method': ['GET'], 'function': check_repo},
735 conditions={'method': ['GET'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
736 requirements=URL_NAME_REQUIREMENTS)
733
737
734 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
738 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
735 controller='admin/repos', action='edit_advanced_locking',
739 controller='admin/repos', action='edit_advanced_locking',
736 conditions={'method': ['PUT'], 'function': check_repo},
740 conditions={'method': ['PUT'], 'function': check_repo},
737 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
738 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
742 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
739 controller='admin/repos', action='toggle_locking',
743 controller='admin/repos', action='toggle_locking',
740 conditions={'method': ['GET'], 'function': check_repo},
744 conditions={'method': ['GET'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
745 requirements=URL_NAME_REQUIREMENTS)
742
746
743 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
747 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
744 controller='admin/repos', action='edit_advanced_journal',
748 controller='admin/repos', action='edit_advanced_journal',
745 conditions={'method': ['PUT'], 'function': check_repo},
749 conditions={'method': ['PUT'], 'function': check_repo},
746 requirements=URL_NAME_REQUIREMENTS)
750 requirements=URL_NAME_REQUIREMENTS)
747
751
748 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
752 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
749 controller='admin/repos', action='edit_advanced_fork',
753 controller='admin/repos', action='edit_advanced_fork',
750 conditions={'method': ['PUT'], 'function': check_repo},
754 conditions={'method': ['PUT'], 'function': check_repo},
751 requirements=URL_NAME_REQUIREMENTS)
755 requirements=URL_NAME_REQUIREMENTS)
752
756
753 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
757 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
754 controller='admin/repos', action='edit_caches_form',
758 controller='admin/repos', action='edit_caches_form',
755 conditions={'method': ['GET'], 'function': check_repo},
759 conditions={'method': ['GET'], 'function': check_repo},
756 requirements=URL_NAME_REQUIREMENTS)
760 requirements=URL_NAME_REQUIREMENTS)
757 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
761 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
758 controller='admin/repos', action='edit_caches',
762 controller='admin/repos', action='edit_caches',
759 conditions={'method': ['PUT'], 'function': check_repo},
763 conditions={'method': ['PUT'], 'function': check_repo},
760 requirements=URL_NAME_REQUIREMENTS)
764 requirements=URL_NAME_REQUIREMENTS)
761
765
762 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
766 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
763 controller='admin/repos', action='edit_remote_form',
767 controller='admin/repos', action='edit_remote_form',
764 conditions={'method': ['GET'], 'function': check_repo},
768 conditions={'method': ['GET'], 'function': check_repo},
765 requirements=URL_NAME_REQUIREMENTS)
769 requirements=URL_NAME_REQUIREMENTS)
766 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
770 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
767 controller='admin/repos', action='edit_remote',
771 controller='admin/repos', action='edit_remote',
768 conditions={'method': ['PUT'], 'function': check_repo},
772 conditions={'method': ['PUT'], 'function': check_repo},
769 requirements=URL_NAME_REQUIREMENTS)
773 requirements=URL_NAME_REQUIREMENTS)
770
774
771 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
775 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
772 controller='admin/repos', action='edit_statistics_form',
776 controller='admin/repos', action='edit_statistics_form',
773 conditions={'method': ['GET'], 'function': check_repo},
777 conditions={'method': ['GET'], 'function': check_repo},
774 requirements=URL_NAME_REQUIREMENTS)
778 requirements=URL_NAME_REQUIREMENTS)
775 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
779 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
776 controller='admin/repos', action='edit_statistics',
780 controller='admin/repos', action='edit_statistics',
777 conditions={'method': ['PUT'], 'function': check_repo},
781 conditions={'method': ['PUT'], 'function': check_repo},
778 requirements=URL_NAME_REQUIREMENTS)
782 requirements=URL_NAME_REQUIREMENTS)
779 rmap.connect('repo_settings_issuetracker',
783 rmap.connect('repo_settings_issuetracker',
780 '/{repo_name}/settings/issue-tracker',
784 '/{repo_name}/settings/issue-tracker',
781 controller='admin/repos', action='repo_issuetracker',
785 controller='admin/repos', action='repo_issuetracker',
782 conditions={'method': ['GET'], 'function': check_repo},
786 conditions={'method': ['GET'], 'function': check_repo},
783 requirements=URL_NAME_REQUIREMENTS)
787 requirements=URL_NAME_REQUIREMENTS)
784 rmap.connect('repo_issuetracker_test',
788 rmap.connect('repo_issuetracker_test',
785 '/{repo_name}/settings/issue-tracker/test',
789 '/{repo_name}/settings/issue-tracker/test',
786 controller='admin/repos', action='repo_issuetracker_test',
790 controller='admin/repos', action='repo_issuetracker_test',
787 conditions={'method': ['POST'], 'function': check_repo},
791 conditions={'method': ['POST'], 'function': check_repo},
788 requirements=URL_NAME_REQUIREMENTS)
792 requirements=URL_NAME_REQUIREMENTS)
789 rmap.connect('repo_issuetracker_delete',
793 rmap.connect('repo_issuetracker_delete',
790 '/{repo_name}/settings/issue-tracker/delete',
794 '/{repo_name}/settings/issue-tracker/delete',
791 controller='admin/repos', action='repo_issuetracker_delete',
795 controller='admin/repos', action='repo_issuetracker_delete',
792 conditions={'method': ['DELETE'], 'function': check_repo},
796 conditions={'method': ['DELETE'], 'function': check_repo},
793 requirements=URL_NAME_REQUIREMENTS)
797 requirements=URL_NAME_REQUIREMENTS)
794 rmap.connect('repo_issuetracker_save',
798 rmap.connect('repo_issuetracker_save',
795 '/{repo_name}/settings/issue-tracker/save',
799 '/{repo_name}/settings/issue-tracker/save',
796 controller='admin/repos', action='repo_issuetracker_save',
800 controller='admin/repos', action='repo_issuetracker_save',
797 conditions={'method': ['POST'], 'function': check_repo},
801 conditions={'method': ['POST'], 'function': check_repo},
798 requirements=URL_NAME_REQUIREMENTS)
802 requirements=URL_NAME_REQUIREMENTS)
799 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
803 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
800 controller='admin/repos', action='repo_settings_vcs_update',
804 controller='admin/repos', action='repo_settings_vcs_update',
801 conditions={'method': ['POST'], 'function': check_repo},
805 conditions={'method': ['POST'], 'function': check_repo},
802 requirements=URL_NAME_REQUIREMENTS)
806 requirements=URL_NAME_REQUIREMENTS)
803 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
807 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
804 controller='admin/repos', action='repo_settings_vcs',
808 controller='admin/repos', action='repo_settings_vcs',
805 conditions={'method': ['GET'], 'function': check_repo},
809 conditions={'method': ['GET'], 'function': check_repo},
806 requirements=URL_NAME_REQUIREMENTS)
810 requirements=URL_NAME_REQUIREMENTS)
807 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
811 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
808 controller='admin/repos', action='repo_delete_svn_pattern',
812 controller='admin/repos', action='repo_delete_svn_pattern',
809 conditions={'method': ['DELETE'], 'function': check_repo},
813 conditions={'method': ['DELETE'], 'function': check_repo},
810 requirements=URL_NAME_REQUIREMENTS)
814 requirements=URL_NAME_REQUIREMENTS)
811 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
815 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
812 controller='admin/repos', action='repo_settings_pullrequest',
816 controller='admin/repos', action='repo_settings_pullrequest',
813 conditions={'method': ['GET', 'POST'], 'function': check_repo},
817 conditions={'method': ['GET', 'POST'], 'function': check_repo},
814 requirements=URL_NAME_REQUIREMENTS)
818 requirements=URL_NAME_REQUIREMENTS)
815
819
816 # still working url for backward compat.
820 # still working url for backward compat.
817 rmap.connect('raw_changeset_home_depraced',
821 rmap.connect('raw_changeset_home_depraced',
818 '/{repo_name}/raw-changeset/{revision}',
822 '/{repo_name}/raw-changeset/{revision}',
819 controller='changeset', action='changeset_raw',
823 controller='changeset', action='changeset_raw',
820 revision='tip', conditions={'function': check_repo},
824 revision='tip', conditions={'function': check_repo},
821 requirements=URL_NAME_REQUIREMENTS)
825 requirements=URL_NAME_REQUIREMENTS)
822
826
823 # new URLs
827 # new URLs
824 rmap.connect('changeset_raw_home',
828 rmap.connect('changeset_raw_home',
825 '/{repo_name}/changeset-diff/{revision}',
829 '/{repo_name}/changeset-diff/{revision}',
826 controller='changeset', action='changeset_raw',
830 controller='changeset', action='changeset_raw',
827 revision='tip', conditions={'function': check_repo},
831 revision='tip', conditions={'function': check_repo},
828 requirements=URL_NAME_REQUIREMENTS)
832 requirements=URL_NAME_REQUIREMENTS)
829
833
830 rmap.connect('changeset_patch_home',
834 rmap.connect('changeset_patch_home',
831 '/{repo_name}/changeset-patch/{revision}',
835 '/{repo_name}/changeset-patch/{revision}',
832 controller='changeset', action='changeset_patch',
836 controller='changeset', action='changeset_patch',
833 revision='tip', conditions={'function': check_repo},
837 revision='tip', conditions={'function': check_repo},
834 requirements=URL_NAME_REQUIREMENTS)
838 requirements=URL_NAME_REQUIREMENTS)
835
839
836 rmap.connect('changeset_download_home',
840 rmap.connect('changeset_download_home',
837 '/{repo_name}/changeset-download/{revision}',
841 '/{repo_name}/changeset-download/{revision}',
838 controller='changeset', action='changeset_download',
842 controller='changeset', action='changeset_download',
839 revision='tip', conditions={'function': check_repo},
843 revision='tip', conditions={'function': check_repo},
840 requirements=URL_NAME_REQUIREMENTS)
844 requirements=URL_NAME_REQUIREMENTS)
841
845
842 rmap.connect('changeset_comment',
846 rmap.connect('changeset_comment',
843 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
847 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
844 controller='changeset', revision='tip', action='comment',
848 controller='changeset', revision='tip', action='comment',
845 conditions={'function': check_repo},
849 conditions={'function': check_repo},
846 requirements=URL_NAME_REQUIREMENTS)
850 requirements=URL_NAME_REQUIREMENTS)
847
851
848 rmap.connect('changeset_comment_preview',
852 rmap.connect('changeset_comment_preview',
849 '/{repo_name}/changeset/comment/preview', jsroute=True,
853 '/{repo_name}/changeset/comment/preview', jsroute=True,
850 controller='changeset', action='preview_comment',
854 controller='changeset', action='preview_comment',
851 conditions={'function': check_repo, 'method': ['POST']},
855 conditions={'function': check_repo, 'method': ['POST']},
852 requirements=URL_NAME_REQUIREMENTS)
856 requirements=URL_NAME_REQUIREMENTS)
853
857
854 rmap.connect('changeset_comment_delete',
858 rmap.connect('changeset_comment_delete',
855 '/{repo_name}/changeset/comment/{comment_id}/delete',
859 '/{repo_name}/changeset/comment/{comment_id}/delete',
856 controller='changeset', action='delete_comment',
860 controller='changeset', action='delete_comment',
857 conditions={'function': check_repo, 'method': ['DELETE']},
861 conditions={'function': check_repo, 'method': ['DELETE']},
858 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
859
863
860 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
864 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
861 controller='changeset', action='changeset_info',
865 controller='changeset', action='changeset_info',
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
866 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
863
867
864 rmap.connect('compare_home',
868 rmap.connect('compare_home',
865 '/{repo_name}/compare',
869 '/{repo_name}/compare',
866 controller='compare', action='index',
870 controller='compare', action='index',
867 conditions={'function': check_repo},
871 conditions={'function': check_repo},
868 requirements=URL_NAME_REQUIREMENTS)
872 requirements=URL_NAME_REQUIREMENTS)
869
873
870 rmap.connect('compare_url',
874 rmap.connect('compare_url',
871 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
875 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
872 controller='compare', action='compare',
876 controller='compare', action='compare',
873 conditions={'function': check_repo},
877 conditions={'function': check_repo},
874 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
878 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
875
879
876 rmap.connect('pullrequest_home',
880 rmap.connect('pullrequest_home',
877 '/{repo_name}/pull-request/new', controller='pullrequests',
881 '/{repo_name}/pull-request/new', controller='pullrequests',
878 action='index', conditions={'function': check_repo,
882 action='index', conditions={'function': check_repo,
879 'method': ['GET']},
883 'method': ['GET']},
880 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
881
885
882 rmap.connect('pullrequest',
886 rmap.connect('pullrequest',
883 '/{repo_name}/pull-request/new', controller='pullrequests',
887 '/{repo_name}/pull-request/new', controller='pullrequests',
884 action='create', conditions={'function': check_repo,
888 action='create', conditions={'function': check_repo,
885 'method': ['POST']},
889 'method': ['POST']},
886 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
890 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
887
891
888 rmap.connect('pullrequest_repo_refs',
892 rmap.connect('pullrequest_repo_refs',
889 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
893 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
890 controller='pullrequests',
894 controller='pullrequests',
891 action='get_repo_refs',
895 action='get_repo_refs',
892 conditions={'function': check_repo, 'method': ['GET']},
896 conditions={'function': check_repo, 'method': ['GET']},
893 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
897 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
894
898
895 rmap.connect('pullrequest_repo_destinations',
899 rmap.connect('pullrequest_repo_destinations',
896 '/{repo_name}/pull-request/repo-destinations',
900 '/{repo_name}/pull-request/repo-destinations',
897 controller='pullrequests',
901 controller='pullrequests',
898 action='get_repo_destinations',
902 action='get_repo_destinations',
899 conditions={'function': check_repo, 'method': ['GET']},
903 conditions={'function': check_repo, 'method': ['GET']},
900 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
904 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
901
905
902 rmap.connect('pullrequest_show',
906 rmap.connect('pullrequest_show',
903 '/{repo_name}/pull-request/{pull_request_id}',
907 '/{repo_name}/pull-request/{pull_request_id}',
904 controller='pullrequests',
908 controller='pullrequests',
905 action='show', conditions={'function': check_repo,
909 action='show', conditions={'function': check_repo,
906 'method': ['GET']},
910 'method': ['GET']},
907 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
908
912
909 rmap.connect('pullrequest_update',
913 rmap.connect('pullrequest_update',
910 '/{repo_name}/pull-request/{pull_request_id}',
914 '/{repo_name}/pull-request/{pull_request_id}',
911 controller='pullrequests',
915 controller='pullrequests',
912 action='update', conditions={'function': check_repo,
916 action='update', conditions={'function': check_repo,
913 'method': ['PUT']},
917 'method': ['PUT']},
914 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
915
919
916 rmap.connect('pullrequest_merge',
920 rmap.connect('pullrequest_merge',
917 '/{repo_name}/pull-request/{pull_request_id}',
921 '/{repo_name}/pull-request/{pull_request_id}',
918 controller='pullrequests',
922 controller='pullrequests',
919 action='merge', conditions={'function': check_repo,
923 action='merge', conditions={'function': check_repo,
920 'method': ['POST']},
924 'method': ['POST']},
921 requirements=URL_NAME_REQUIREMENTS)
925 requirements=URL_NAME_REQUIREMENTS)
922
926
923 rmap.connect('pullrequest_delete',
927 rmap.connect('pullrequest_delete',
924 '/{repo_name}/pull-request/{pull_request_id}',
928 '/{repo_name}/pull-request/{pull_request_id}',
925 controller='pullrequests',
929 controller='pullrequests',
926 action='delete', conditions={'function': check_repo,
930 action='delete', conditions={'function': check_repo,
927 'method': ['DELETE']},
931 'method': ['DELETE']},
928 requirements=URL_NAME_REQUIREMENTS)
932 requirements=URL_NAME_REQUIREMENTS)
929
933
930 rmap.connect('pullrequest_show_all',
934 rmap.connect('pullrequest_show_all',
931 '/{repo_name}/pull-request',
935 '/{repo_name}/pull-request',
932 controller='pullrequests',
936 controller='pullrequests',
933 action='show_all', conditions={'function': check_repo,
937 action='show_all', conditions={'function': check_repo,
934 'method': ['GET']},
938 'method': ['GET']},
935 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
939 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
936
940
937 rmap.connect('pullrequest_comment',
941 rmap.connect('pullrequest_comment',
938 '/{repo_name}/pull-request-comment/{pull_request_id}',
942 '/{repo_name}/pull-request-comment/{pull_request_id}',
939 controller='pullrequests',
943 controller='pullrequests',
940 action='comment', conditions={'function': check_repo,
944 action='comment', conditions={'function': check_repo,
941 'method': ['POST']},
945 'method': ['POST']},
942 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
943
947
944 rmap.connect('pullrequest_comment_delete',
948 rmap.connect('pullrequest_comment_delete',
945 '/{repo_name}/pull-request-comment/{comment_id}/delete',
949 '/{repo_name}/pull-request-comment/{comment_id}/delete',
946 controller='pullrequests', action='delete_comment',
950 controller='pullrequests', action='delete_comment',
947 conditions={'function': check_repo, 'method': ['DELETE']},
951 conditions={'function': check_repo, 'method': ['DELETE']},
948 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
952 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
949
953
950 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
954 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
951 controller='summary', conditions={'function': check_repo},
955 controller='summary', conditions={'function': check_repo},
952 requirements=URL_NAME_REQUIREMENTS)
956 requirements=URL_NAME_REQUIREMENTS)
953
957
954 rmap.connect('branches_home', '/{repo_name}/branches',
958 rmap.connect('branches_home', '/{repo_name}/branches',
955 controller='branches', conditions={'function': check_repo},
959 controller='branches', conditions={'function': check_repo},
956 requirements=URL_NAME_REQUIREMENTS)
960 requirements=URL_NAME_REQUIREMENTS)
957
961
958 rmap.connect('tags_home', '/{repo_name}/tags',
962 rmap.connect('tags_home', '/{repo_name}/tags',
959 controller='tags', conditions={'function': check_repo},
963 controller='tags', conditions={'function': check_repo},
960 requirements=URL_NAME_REQUIREMENTS)
964 requirements=URL_NAME_REQUIREMENTS)
961
965
962 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
966 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
963 controller='bookmarks', conditions={'function': check_repo},
967 controller='bookmarks', conditions={'function': check_repo},
964 requirements=URL_NAME_REQUIREMENTS)
968 requirements=URL_NAME_REQUIREMENTS)
965
969
966 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
970 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
967 controller='changelog', conditions={'function': check_repo},
971 controller='changelog', conditions={'function': check_repo},
968 requirements=URL_NAME_REQUIREMENTS)
972 requirements=URL_NAME_REQUIREMENTS)
969
973
970 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
974 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
971 controller='changelog', action='changelog_summary',
975 controller='changelog', action='changelog_summary',
972 conditions={'function': check_repo},
976 conditions={'function': check_repo},
973 requirements=URL_NAME_REQUIREMENTS)
977 requirements=URL_NAME_REQUIREMENTS)
974
978
975 rmap.connect('changelog_file_home',
979 rmap.connect('changelog_file_home',
976 '/{repo_name}/changelog/{revision}/{f_path}',
980 '/{repo_name}/changelog/{revision}/{f_path}',
977 controller='changelog', f_path=None,
981 controller='changelog', f_path=None,
978 conditions={'function': check_repo},
982 conditions={'function': check_repo},
979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
983 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
980
984
981 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
985 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
982 controller='changelog', action='changelog_elements',
986 controller='changelog', action='changelog_elements',
983 conditions={'function': check_repo},
987 conditions={'function': check_repo},
984 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
988 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
985
989
986 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
990 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
987 controller='files', revision='tip', f_path='',
991 controller='files', revision='tip', f_path='',
988 conditions={'function': check_repo},
992 conditions={'function': check_repo},
989 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
993 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
990
994
991 rmap.connect('files_home_simple_catchrev',
995 rmap.connect('files_home_simple_catchrev',
992 '/{repo_name}/files/{revision}',
996 '/{repo_name}/files/{revision}',
993 controller='files', revision='tip', f_path='',
997 controller='files', revision='tip', f_path='',
994 conditions={'function': check_repo},
998 conditions={'function': check_repo},
995 requirements=URL_NAME_REQUIREMENTS)
999 requirements=URL_NAME_REQUIREMENTS)
996
1000
997 rmap.connect('files_home_simple_catchall',
1001 rmap.connect('files_home_simple_catchall',
998 '/{repo_name}/files',
1002 '/{repo_name}/files',
999 controller='files', revision='tip', f_path='',
1003 controller='files', revision='tip', f_path='',
1000 conditions={'function': check_repo},
1004 conditions={'function': check_repo},
1001 requirements=URL_NAME_REQUIREMENTS)
1005 requirements=URL_NAME_REQUIREMENTS)
1002
1006
1003 rmap.connect('files_history_home',
1007 rmap.connect('files_history_home',
1004 '/{repo_name}/history/{revision}/{f_path}',
1008 '/{repo_name}/history/{revision}/{f_path}',
1005 controller='files', action='history', revision='tip', f_path='',
1009 controller='files', action='history', revision='tip', f_path='',
1006 conditions={'function': check_repo},
1010 conditions={'function': check_repo},
1007 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1011 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1008
1012
1009 rmap.connect('files_authors_home',
1013 rmap.connect('files_authors_home',
1010 '/{repo_name}/authors/{revision}/{f_path}',
1014 '/{repo_name}/authors/{revision}/{f_path}',
1011 controller='files', action='authors', revision='tip', f_path='',
1015 controller='files', action='authors', revision='tip', f_path='',
1012 conditions={'function': check_repo},
1016 conditions={'function': check_repo},
1013 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1017 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1014
1018
1015 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1019 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1016 controller='files', action='diff', f_path='',
1020 controller='files', action='diff', f_path='',
1017 conditions={'function': check_repo},
1021 conditions={'function': check_repo},
1018 requirements=URL_NAME_REQUIREMENTS)
1022 requirements=URL_NAME_REQUIREMENTS)
1019
1023
1020 rmap.connect('files_diff_2way_home',
1024 rmap.connect('files_diff_2way_home',
1021 '/{repo_name}/diff-2way/{f_path}',
1025 '/{repo_name}/diff-2way/{f_path}',
1022 controller='files', action='diff_2way', f_path='',
1026 controller='files', action='diff_2way', f_path='',
1023 conditions={'function': check_repo},
1027 conditions={'function': check_repo},
1024 requirements=URL_NAME_REQUIREMENTS)
1028 requirements=URL_NAME_REQUIREMENTS)
1025
1029
1026 rmap.connect('files_rawfile_home',
1030 rmap.connect('files_rawfile_home',
1027 '/{repo_name}/rawfile/{revision}/{f_path}',
1031 '/{repo_name}/rawfile/{revision}/{f_path}',
1028 controller='files', action='rawfile', revision='tip',
1032 controller='files', action='rawfile', revision='tip',
1029 f_path='', conditions={'function': check_repo},
1033 f_path='', conditions={'function': check_repo},
1030 requirements=URL_NAME_REQUIREMENTS)
1034 requirements=URL_NAME_REQUIREMENTS)
1031
1035
1032 rmap.connect('files_raw_home',
1036 rmap.connect('files_raw_home',
1033 '/{repo_name}/raw/{revision}/{f_path}',
1037 '/{repo_name}/raw/{revision}/{f_path}',
1034 controller='files', action='raw', revision='tip', f_path='',
1038 controller='files', action='raw', revision='tip', f_path='',
1035 conditions={'function': check_repo},
1039 conditions={'function': check_repo},
1036 requirements=URL_NAME_REQUIREMENTS)
1040 requirements=URL_NAME_REQUIREMENTS)
1037
1041
1038 rmap.connect('files_render_home',
1042 rmap.connect('files_render_home',
1039 '/{repo_name}/render/{revision}/{f_path}',
1043 '/{repo_name}/render/{revision}/{f_path}',
1040 controller='files', action='index', revision='tip', f_path='',
1044 controller='files', action='index', revision='tip', f_path='',
1041 rendered=True, conditions={'function': check_repo},
1045 rendered=True, conditions={'function': check_repo},
1042 requirements=URL_NAME_REQUIREMENTS)
1046 requirements=URL_NAME_REQUIREMENTS)
1043
1047
1044 rmap.connect('files_annotate_home',
1048 rmap.connect('files_annotate_home',
1045 '/{repo_name}/annotate/{revision}/{f_path}',
1049 '/{repo_name}/annotate/{revision}/{f_path}',
1046 controller='files', action='index', revision='tip',
1050 controller='files', action='index', revision='tip',
1047 f_path='', annotate=True, conditions={'function': check_repo},
1051 f_path='', annotate=True, conditions={'function': check_repo},
1048 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1052 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1049
1053
1050 rmap.connect('files_annotate_previous',
1054 rmap.connect('files_annotate_previous',
1051 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1055 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1052 controller='files', action='annotate_previous', revision='tip',
1056 controller='files', action='annotate_previous', revision='tip',
1053 f_path='', annotate=True, conditions={'function': check_repo},
1057 f_path='', annotate=True, conditions={'function': check_repo},
1054 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1058 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1055
1059
1056 rmap.connect('files_edit',
1060 rmap.connect('files_edit',
1057 '/{repo_name}/edit/{revision}/{f_path}',
1061 '/{repo_name}/edit/{revision}/{f_path}',
1058 controller='files', action='edit', revision='tip',
1062 controller='files', action='edit', revision='tip',
1059 f_path='',
1063 f_path='',
1060 conditions={'function': check_repo, 'method': ['POST']},
1064 conditions={'function': check_repo, 'method': ['POST']},
1061 requirements=URL_NAME_REQUIREMENTS)
1065 requirements=URL_NAME_REQUIREMENTS)
1062
1066
1063 rmap.connect('files_edit_home',
1067 rmap.connect('files_edit_home',
1064 '/{repo_name}/edit/{revision}/{f_path}',
1068 '/{repo_name}/edit/{revision}/{f_path}',
1065 controller='files', action='edit_home', revision='tip',
1069 controller='files', action='edit_home', revision='tip',
1066 f_path='', conditions={'function': check_repo},
1070 f_path='', conditions={'function': check_repo},
1067 requirements=URL_NAME_REQUIREMENTS)
1071 requirements=URL_NAME_REQUIREMENTS)
1068
1072
1069 rmap.connect('files_add',
1073 rmap.connect('files_add',
1070 '/{repo_name}/add/{revision}/{f_path}',
1074 '/{repo_name}/add/{revision}/{f_path}',
1071 controller='files', action='add', revision='tip',
1075 controller='files', action='add', revision='tip',
1072 f_path='',
1076 f_path='',
1073 conditions={'function': check_repo, 'method': ['POST']},
1077 conditions={'function': check_repo, 'method': ['POST']},
1074 requirements=URL_NAME_REQUIREMENTS)
1078 requirements=URL_NAME_REQUIREMENTS)
1075
1079
1076 rmap.connect('files_add_home',
1080 rmap.connect('files_add_home',
1077 '/{repo_name}/add/{revision}/{f_path}',
1081 '/{repo_name}/add/{revision}/{f_path}',
1078 controller='files', action='add_home', revision='tip',
1082 controller='files', action='add_home', revision='tip',
1079 f_path='', conditions={'function': check_repo},
1083 f_path='', conditions={'function': check_repo},
1080 requirements=URL_NAME_REQUIREMENTS)
1084 requirements=URL_NAME_REQUIREMENTS)
1081
1085
1082 rmap.connect('files_delete',
1086 rmap.connect('files_delete',
1083 '/{repo_name}/delete/{revision}/{f_path}',
1087 '/{repo_name}/delete/{revision}/{f_path}',
1084 controller='files', action='delete', revision='tip',
1088 controller='files', action='delete', revision='tip',
1085 f_path='',
1089 f_path='',
1086 conditions={'function': check_repo, 'method': ['POST']},
1090 conditions={'function': check_repo, 'method': ['POST']},
1087 requirements=URL_NAME_REQUIREMENTS)
1091 requirements=URL_NAME_REQUIREMENTS)
1088
1092
1089 rmap.connect('files_delete_home',
1093 rmap.connect('files_delete_home',
1090 '/{repo_name}/delete/{revision}/{f_path}',
1094 '/{repo_name}/delete/{revision}/{f_path}',
1091 controller='files', action='delete_home', revision='tip',
1095 controller='files', action='delete_home', revision='tip',
1092 f_path='', conditions={'function': check_repo},
1096 f_path='', conditions={'function': check_repo},
1093 requirements=URL_NAME_REQUIREMENTS)
1097 requirements=URL_NAME_REQUIREMENTS)
1094
1098
1095 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1099 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1096 controller='files', action='archivefile',
1100 controller='files', action='archivefile',
1097 conditions={'function': check_repo},
1101 conditions={'function': check_repo},
1098 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1102 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1099
1103
1100 rmap.connect('files_nodelist_home',
1104 rmap.connect('files_nodelist_home',
1101 '/{repo_name}/nodelist/{revision}/{f_path}',
1105 '/{repo_name}/nodelist/{revision}/{f_path}',
1102 controller='files', action='nodelist',
1106 controller='files', action='nodelist',
1103 conditions={'function': check_repo},
1107 conditions={'function': check_repo},
1104 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1108 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1105
1109
1106 rmap.connect('files_nodetree_full',
1110 rmap.connect('files_nodetree_full',
1107 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1111 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1108 controller='files', action='nodetree_full',
1112 controller='files', action='nodetree_full',
1109 conditions={'function': check_repo},
1113 conditions={'function': check_repo},
1110 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1114 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1111
1115
1112 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1116 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1113 controller='forks', action='fork_create',
1117 controller='forks', action='fork_create',
1114 conditions={'function': check_repo, 'method': ['POST']},
1118 conditions={'function': check_repo, 'method': ['POST']},
1115 requirements=URL_NAME_REQUIREMENTS)
1119 requirements=URL_NAME_REQUIREMENTS)
1116
1120
1117 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1121 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1118 controller='forks', action='fork',
1122 controller='forks', action='fork',
1119 conditions={'function': check_repo},
1123 conditions={'function': check_repo},
1120 requirements=URL_NAME_REQUIREMENTS)
1124 requirements=URL_NAME_REQUIREMENTS)
1121
1125
1122 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1126 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1123 controller='forks', action='forks',
1127 controller='forks', action='forks',
1124 conditions={'function': check_repo},
1128 conditions={'function': check_repo},
1125 requirements=URL_NAME_REQUIREMENTS)
1129 requirements=URL_NAME_REQUIREMENTS)
1126
1130
1127 # must be here for proper group/repo catching pattern
1131 # must be here for proper group/repo catching pattern
1128 _connect_with_slash(
1132 _connect_with_slash(
1129 rmap, 'repo_group_home', '/{group_name}',
1133 rmap, 'repo_group_home', '/{group_name}',
1130 controller='home', action='index_repo_group',
1134 controller='home', action='index_repo_group',
1131 conditions={'function': check_group},
1135 conditions={'function': check_group},
1132 requirements=URL_NAME_REQUIREMENTS)
1136 requirements=URL_NAME_REQUIREMENTS)
1133
1137
1134 # catch all, at the end
1138 # catch all, at the end
1135 _connect_with_slash(
1139 _connect_with_slash(
1136 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1140 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1137 controller='summary', action='index',
1141 controller='summary', action='index',
1138 conditions={'function': check_repo},
1142 conditions={'function': check_repo},
1139 requirements=URL_NAME_REQUIREMENTS)
1143 requirements=URL_NAME_REQUIREMENTS)
1140
1144
1141 return rmap
1145 return rmap
1142
1146
1143
1147
1144 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1148 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1145 """
1149 """
1146 Connect a route with an optional trailing slash in `path`.
1150 Connect a route with an optional trailing slash in `path`.
1147 """
1151 """
1148 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1152 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1149 mapper.connect(name, path, *args, **kwargs)
1153 mapper.connect(name, path, *args, **kwargs)
@@ -1,487 +1,517 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-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 User Groups crud controller for pylons
22 User Groups crud controller for pylons
23 """
23 """
24
24
25 import logging
25 import logging
26 import formencode
26 import formencode
27
27
28 import peppercorn
28 import peppercorn
29 from formencode import htmlfill
29 from formencode import htmlfill
30 from pylons import request, tmpl_context as c, url, config
30 from pylons import request, tmpl_context as c, url, config
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35
35
36 from rhodecode.lib import auth
36 from rhodecode.lib import auth
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.exceptions import UserGroupAssignedException,\
38 from rhodecode.lib.exceptions import UserGroupAssignedException,\
39 RepoGroupAssignmentError
39 RepoGroupAssignmentError
40 from rhodecode.lib.utils import jsonify, action_logger
40 from rhodecode.lib.utils import jsonify, action_logger
41 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
41 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
42 from rhodecode.lib.auth import (
42 from rhodecode.lib.auth import (
43 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
43 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
44 HasPermissionAnyDecorator, XHRRequired)
44 HasPermissionAnyDecorator, XHRRequired)
45 from rhodecode.lib.base import BaseController, render
45 from rhodecode.lib.base import BaseController, render
46 from rhodecode.model.permission import PermissionModel
46 from rhodecode.model.permission import PermissionModel
47 from rhodecode.model.scm import UserGroupList
47 from rhodecode.model.scm import UserGroupList
48 from rhodecode.model.user_group import UserGroupModel
48 from rhodecode.model.user_group import UserGroupModel
49 from rhodecode.model.db import (
49 from rhodecode.model.db import (
50 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
50 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
51 from rhodecode.model.forms import (
51 from rhodecode.model.forms import (
52 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
52 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
53 UserPermissionsForm)
53 UserPermissionsForm)
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55 from rhodecode.lib.utils import action_logger
55 from rhodecode.lib.utils import action_logger
56 from rhodecode.lib.ext_json import json
56 from rhodecode.lib.ext_json import json
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class UserGroupsController(BaseController):
61 class UserGroupsController(BaseController):
62 """REST Controller styled on the Atom Publishing Protocol"""
62 """REST Controller styled on the Atom Publishing Protocol"""
63
63
64 @LoginRequired()
64 @LoginRequired()
65 def __before__(self):
65 def __before__(self):
66 super(UserGroupsController, self).__before__()
66 super(UserGroupsController, self).__before__()
67 c.available_permissions = config['available_permissions']
67 c.available_permissions = config['available_permissions']
68 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
68 PermissionModel().set_global_permission_choices(c, gettext_translator=_)
69
69
70 def __load_data(self, user_group_id):
70 def __load_data(self, user_group_id):
71 c.group_members_obj = [x.user for x in c.user_group.members]
71 c.group_members_obj = [x.user for x in c.user_group.members]
72 c.group_members_obj.sort(key=lambda u: u.username.lower())
72 c.group_members_obj.sort(key=lambda u: u.username.lower())
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
73 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
74
74
75 def __load_defaults(self, user_group_id):
75 def __load_defaults(self, user_group_id):
76 """
76 """
77 Load defaults settings for edit, and update
77 Load defaults settings for edit, and update
78
78
79 :param user_group_id:
79 :param user_group_id:
80 """
80 """
81 user_group = UserGroup.get_or_404(user_group_id)
81 user_group = UserGroup.get_or_404(user_group_id)
82 data = user_group.get_dict()
82 data = user_group.get_dict()
83 # fill owner
83 # fill owner
84 if user_group.user:
84 if user_group.user:
85 data.update({'user': user_group.user.username})
85 data.update({'user': user_group.user.username})
86 else:
86 else:
87 replacement_user = User.get_first_super_admin().username
87 replacement_user = User.get_first_super_admin().username
88 data.update({'user': replacement_user})
88 data.update({'user': replacement_user})
89 return data
89 return data
90
90
91 def _revoke_perms_on_yourself(self, form_result):
91 def _revoke_perms_on_yourself(self, form_result):
92 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
92 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
93 form_result['perm_updates'])
93 form_result['perm_updates'])
94 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
94 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
95 form_result['perm_additions'])
95 form_result['perm_additions'])
96 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
96 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
97 form_result['perm_deletions'])
97 form_result['perm_deletions'])
98 admin_perm = 'usergroup.admin'
98 admin_perm = 'usergroup.admin'
99 if _updates and _updates[0][1] != admin_perm or \
99 if _updates and _updates[0][1] != admin_perm or \
100 _additions and _additions[0][1] != admin_perm or \
100 _additions and _additions[0][1] != admin_perm or \
101 _deletions and _deletions[0][1] != admin_perm:
101 _deletions and _deletions[0][1] != admin_perm:
102 return True
102 return True
103 return False
103 return False
104
104
105 # permission check inside
105 # permission check inside
106 @NotAnonymous()
106 @NotAnonymous()
107 def index(self):
107 def index(self):
108 """GET /users_groups: All items in the collection"""
108 """GET /users_groups: All items in the collection"""
109 # url('users_groups')
109 # url('users_groups')
110
110
111 from rhodecode.lib.utils import PartialRenderer
111 from rhodecode.lib.utils import PartialRenderer
112 _render = PartialRenderer('data_table/_dt_elements.mako')
112 _render = PartialRenderer('data_table/_dt_elements.mako')
113
113
114 def user_group_name(user_group_id, user_group_name):
114 def user_group_name(user_group_id, user_group_name):
115 return _render("user_group_name", user_group_id, user_group_name)
115 return _render("user_group_name", user_group_id, user_group_name)
116
116
117 def user_group_actions(user_group_id, user_group_name):
117 def user_group_actions(user_group_id, user_group_name):
118 return _render("user_group_actions", user_group_id, user_group_name)
118 return _render("user_group_actions", user_group_id, user_group_name)
119
119
120 ## json generate
120 # json generate
121 group_iter = UserGroupList(UserGroup.query().all(),
121 group_iter = UserGroupList(UserGroup.query().all(),
122 perm_set=['usergroup.admin'])
122 perm_set=['usergroup.admin'])
123
123
124 user_groups_data = []
124 user_groups_data = []
125 for user_gr in group_iter:
125 for user_gr in group_iter:
126 user_groups_data.append({
126 user_groups_data.append({
127 "group_name": user_group_name(
127 "group_name": user_group_name(
128 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
128 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
129 "group_name_raw": user_gr.users_group_name,
129 "group_name_raw": user_gr.users_group_name,
130 "desc": h.escape(user_gr.user_group_description),
130 "desc": h.escape(user_gr.user_group_description),
131 "members": len(user_gr.members),
131 "members": len(user_gr.members),
132 "sync": user_gr.group_data.get('extern_type'),
132 "active": h.bool2icon(user_gr.users_group_active),
133 "active": h.bool2icon(user_gr.users_group_active),
133 "owner": h.escape(h.link_to_user(user_gr.user.username)),
134 "owner": h.escape(h.link_to_user(user_gr.user.username)),
134 "action": user_group_actions(
135 "action": user_group_actions(
135 user_gr.users_group_id, user_gr.users_group_name)
136 user_gr.users_group_id, user_gr.users_group_name)
136 })
137 })
137
138
138 c.data = json.dumps(user_groups_data)
139 c.data = json.dumps(user_groups_data)
139 return render('admin/user_groups/user_groups.mako')
140 return render('admin/user_groups/user_groups.mako')
140
141
141 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
142 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
142 @auth.CSRFRequired()
143 @auth.CSRFRequired()
143 def create(self):
144 def create(self):
144 """POST /users_groups: Create a new item"""
145 """POST /users_groups: Create a new item"""
145 # url('users_groups')
146 # url('users_groups')
146
147
147 users_group_form = UserGroupForm()()
148 users_group_form = UserGroupForm()()
148 try:
149 try:
149 form_result = users_group_form.to_python(dict(request.POST))
150 form_result = users_group_form.to_python(dict(request.POST))
150 user_group = UserGroupModel().create(
151 user_group = UserGroupModel().create(
151 name=form_result['users_group_name'],
152 name=form_result['users_group_name'],
152 description=form_result['user_group_description'],
153 description=form_result['user_group_description'],
153 owner=c.rhodecode_user.user_id,
154 owner=c.rhodecode_user.user_id,
154 active=form_result['users_group_active'])
155 active=form_result['users_group_active'])
155 Session().flush()
156 Session().flush()
156
157
157 user_group_name = form_result['users_group_name']
158 user_group_name = form_result['users_group_name']
158 action_logger(c.rhodecode_user,
159 action_logger(c.rhodecode_user,
159 'admin_created_users_group:%s' % user_group_name,
160 'admin_created_users_group:%s' % user_group_name,
160 None, self.ip_addr, self.sa)
161 None, self.ip_addr, self.sa)
161 user_group_link = h.link_to(h.escape(user_group_name),
162 user_group_link = h.link_to(h.escape(user_group_name),
162 url('edit_users_group',
163 url('edit_users_group',
163 user_group_id=user_group.users_group_id))
164 user_group_id=user_group.users_group_id))
164 h.flash(h.literal(_('Created user group %(user_group_link)s')
165 h.flash(h.literal(_('Created user group %(user_group_link)s')
165 % {'user_group_link': user_group_link}),
166 % {'user_group_link': user_group_link}),
166 category='success')
167 category='success')
167 Session().commit()
168 Session().commit()
168 except formencode.Invalid as errors:
169 except formencode.Invalid as errors:
169 return htmlfill.render(
170 return htmlfill.render(
170 render('admin/user_groups/user_group_add.mako'),
171 render('admin/user_groups/user_group_add.mako'),
171 defaults=errors.value,
172 defaults=errors.value,
172 errors=errors.error_dict or {},
173 errors=errors.error_dict or {},
173 prefix_error=False,
174 prefix_error=False,
174 encoding="UTF-8",
175 encoding="UTF-8",
175 force_defaults=False)
176 force_defaults=False)
176 except Exception:
177 except Exception:
177 log.exception("Exception creating user group")
178 log.exception("Exception creating user group")
178 h.flash(_('Error occurred during creation of user group %s') \
179 h.flash(_('Error occurred during creation of user group %s') \
179 % request.POST.get('users_group_name'), category='error')
180 % request.POST.get('users_group_name'), category='error')
180
181
181 return redirect(
182 return redirect(
182 url('edit_users_group', user_group_id=user_group.users_group_id))
183 url('edit_users_group', user_group_id=user_group.users_group_id))
183
184
184 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
185 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
185 def new(self):
186 def new(self):
186 """GET /user_groups/new: Form to create a new item"""
187 """GET /user_groups/new: Form to create a new item"""
187 # url('new_users_group')
188 # url('new_users_group')
188 return render('admin/user_groups/user_group_add.mako')
189 return render('admin/user_groups/user_group_add.mako')
189
190
190 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
191 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
191 @auth.CSRFRequired()
192 @auth.CSRFRequired()
192 def update(self, user_group_id):
193 def update(self, user_group_id):
193 """PUT /user_groups/user_group_id: Update an existing item"""
194 """PUT /user_groups/user_group_id: Update an existing item"""
194 # Forms posted to this method should contain a hidden field:
195 # Forms posted to this method should contain a hidden field:
195 # <input type="hidden" name="_method" value="PUT" />
196 # <input type="hidden" name="_method" value="PUT" />
196 # Or using helpers:
197 # Or using helpers:
197 # h.form(url('users_group', user_group_id=ID),
198 # h.form(url('users_group', user_group_id=ID),
198 # method='put')
199 # method='put')
199 # url('users_group', user_group_id=ID)
200 # url('users_group', user_group_id=ID)
200
201
201 user_group_id = safe_int(user_group_id)
202 user_group_id = safe_int(user_group_id)
202 c.user_group = UserGroup.get_or_404(user_group_id)
203 c.user_group = UserGroup.get_or_404(user_group_id)
203 c.active = 'settings'
204 c.active = 'settings'
204 self.__load_data(user_group_id)
205 self.__load_data(user_group_id)
205
206
206 users_group_form = UserGroupForm(
207 users_group_form = UserGroupForm(
207 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
208 edit=True, old_data=c.user_group.get_dict(), allow_disabled=True)()
208
209
209 try:
210 try:
210 form_result = users_group_form.to_python(request.POST)
211 form_result = users_group_form.to_python(request.POST)
211 pstruct = peppercorn.parse(request.POST.items())
212 pstruct = peppercorn.parse(request.POST.items())
212 form_result['users_group_members'] = pstruct['user_group_members']
213 form_result['users_group_members'] = pstruct['user_group_members']
213
214
214 UserGroupModel().update(c.user_group, form_result)
215 UserGroupModel().update(c.user_group, form_result)
215 updated_user_group = form_result['users_group_name']
216 updated_user_group = form_result['users_group_name']
216 action_logger(c.rhodecode_user,
217 action_logger(c.rhodecode_user,
217 'admin_updated_users_group:%s' % updated_user_group,
218 'admin_updated_users_group:%s' % updated_user_group,
218 None, self.ip_addr, self.sa)
219 None, self.ip_addr, self.sa)
219 h.flash(_('Updated user group %s') % updated_user_group,
220 h.flash(_('Updated user group %s') % updated_user_group,
220 category='success')
221 category='success')
221 Session().commit()
222 Session().commit()
222 except formencode.Invalid as errors:
223 except formencode.Invalid as errors:
223 defaults = errors.value
224 defaults = errors.value
224 e = errors.error_dict or {}
225 e = errors.error_dict or {}
225
226
226 return htmlfill.render(
227 return htmlfill.render(
227 render('admin/user_groups/user_group_edit.mako'),
228 render('admin/user_groups/user_group_edit.mako'),
228 defaults=defaults,
229 defaults=defaults,
229 errors=e,
230 errors=e,
230 prefix_error=False,
231 prefix_error=False,
231 encoding="UTF-8",
232 encoding="UTF-8",
232 force_defaults=False)
233 force_defaults=False)
233 except Exception:
234 except Exception:
234 log.exception("Exception during update of user group")
235 log.exception("Exception during update of user group")
235 h.flash(_('Error occurred during update of user group %s')
236 h.flash(_('Error occurred during update of user group %s')
236 % request.POST.get('users_group_name'), category='error')
237 % request.POST.get('users_group_name'), category='error')
237
238
238 return redirect(url('edit_users_group', user_group_id=user_group_id))
239 return redirect(url('edit_users_group', user_group_id=user_group_id))
239
240
240 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
241 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
241 @auth.CSRFRequired()
242 @auth.CSRFRequired()
242 def delete(self, user_group_id):
243 def delete(self, user_group_id):
243 """DELETE /user_groups/user_group_id: Delete an existing item"""
244 """DELETE /user_groups/user_group_id: Delete an existing item"""
244 # Forms posted to this method should contain a hidden field:
245 # Forms posted to this method should contain a hidden field:
245 # <input type="hidden" name="_method" value="DELETE" />
246 # <input type="hidden" name="_method" value="DELETE" />
246 # Or using helpers:
247 # Or using helpers:
247 # h.form(url('users_group', user_group_id=ID),
248 # h.form(url('users_group', user_group_id=ID),
248 # method='delete')
249 # method='delete')
249 # url('users_group', user_group_id=ID)
250 # url('users_group', user_group_id=ID)
250 user_group_id = safe_int(user_group_id)
251 user_group_id = safe_int(user_group_id)
251 c.user_group = UserGroup.get_or_404(user_group_id)
252 c.user_group = UserGroup.get_or_404(user_group_id)
252 force = str2bool(request.POST.get('force'))
253 force = str2bool(request.POST.get('force'))
253
254
254 try:
255 try:
255 UserGroupModel().delete(c.user_group, force=force)
256 UserGroupModel().delete(c.user_group, force=force)
256 Session().commit()
257 Session().commit()
257 h.flash(_('Successfully deleted user group'), category='success')
258 h.flash(_('Successfully deleted user group'), category='success')
258 except UserGroupAssignedException as e:
259 except UserGroupAssignedException as e:
259 h.flash(str(e), category='error')
260 h.flash(str(e), category='error')
260 except Exception:
261 except Exception:
261 log.exception("Exception during deletion of user group")
262 log.exception("Exception during deletion of user group")
262 h.flash(_('An error occurred during deletion of user group'),
263 h.flash(_('An error occurred during deletion of user group'),
263 category='error')
264 category='error')
264 return redirect(url('users_groups'))
265 return redirect(url('users_groups'))
265
266
266 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
267 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
267 def edit(self, user_group_id):
268 def edit(self, user_group_id):
268 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
269 """GET /user_groups/user_group_id/edit: Form to edit an existing item"""
269 # url('edit_users_group', user_group_id=ID)
270 # url('edit_users_group', user_group_id=ID)
270
271
271 user_group_id = safe_int(user_group_id)
272 user_group_id = safe_int(user_group_id)
272 c.user_group = UserGroup.get_or_404(user_group_id)
273 c.user_group = UserGroup.get_or_404(user_group_id)
273 c.active = 'settings'
274 c.active = 'settings'
274 self.__load_data(user_group_id)
275 self.__load_data(user_group_id)
275
276
276 defaults = self.__load_defaults(user_group_id)
277 defaults = self.__load_defaults(user_group_id)
277
278
278 return htmlfill.render(
279 return htmlfill.render(
279 render('admin/user_groups/user_group_edit.mako'),
280 render('admin/user_groups/user_group_edit.mako'),
280 defaults=defaults,
281 defaults=defaults,
281 encoding="UTF-8",
282 encoding="UTF-8",
282 force_defaults=False
283 force_defaults=False
283 )
284 )
284
285
285 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
286 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
286 def edit_perms(self, user_group_id):
287 def edit_perms(self, user_group_id):
287 user_group_id = safe_int(user_group_id)
288 user_group_id = safe_int(user_group_id)
288 c.user_group = UserGroup.get_or_404(user_group_id)
289 c.user_group = UserGroup.get_or_404(user_group_id)
289 c.active = 'perms'
290 c.active = 'perms'
290
291
291 defaults = {}
292 defaults = {}
292 # fill user group users
293 # fill user group users
293 for p in c.user_group.user_user_group_to_perm:
294 for p in c.user_group.user_user_group_to_perm:
294 defaults.update({'u_perm_%s' % p.user.user_id:
295 defaults.update({'u_perm_%s' % p.user.user_id:
295 p.permission.permission_name})
296 p.permission.permission_name})
296
297
297 for p in c.user_group.user_group_user_group_to_perm:
298 for p in c.user_group.user_group_user_group_to_perm:
298 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
299 defaults.update({'g_perm_%s' % p.user_group.users_group_id:
299 p.permission.permission_name})
300 p.permission.permission_name})
300
301
301 return htmlfill.render(
302 return htmlfill.render(
302 render('admin/user_groups/user_group_edit.mako'),
303 render('admin/user_groups/user_group_edit.mako'),
303 defaults=defaults,
304 defaults=defaults,
304 encoding="UTF-8",
305 encoding="UTF-8",
305 force_defaults=False
306 force_defaults=False
306 )
307 )
307
308
308 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
309 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
309 @auth.CSRFRequired()
310 @auth.CSRFRequired()
310 def update_perms(self, user_group_id):
311 def update_perms(self, user_group_id):
311 """
312 """
312 grant permission for given usergroup
313 grant permission for given usergroup
313
314
314 :param user_group_id:
315 :param user_group_id:
315 """
316 """
316 user_group_id = safe_int(user_group_id)
317 user_group_id = safe_int(user_group_id)
317 c.user_group = UserGroup.get_or_404(user_group_id)
318 c.user_group = UserGroup.get_or_404(user_group_id)
318 form = UserGroupPermsForm()().to_python(request.POST)
319 form = UserGroupPermsForm()().to_python(request.POST)
319
320
320 if not c.rhodecode_user.is_admin:
321 if not c.rhodecode_user.is_admin:
321 if self._revoke_perms_on_yourself(form):
322 if self._revoke_perms_on_yourself(form):
322 msg = _('Cannot change permission for yourself as admin')
323 msg = _('Cannot change permission for yourself as admin')
323 h.flash(msg, category='warning')
324 h.flash(msg, category='warning')
324 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
325 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
325
326
326 try:
327 try:
327 UserGroupModel().update_permissions(user_group_id,
328 UserGroupModel().update_permissions(user_group_id,
328 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
329 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
329 except RepoGroupAssignmentError:
330 except RepoGroupAssignmentError:
330 h.flash(_('Target group cannot be the same'), category='error')
331 h.flash(_('Target group cannot be the same'), category='error')
331 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
332 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
332 #TODO: implement this
333 #TODO: implement this
333 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
334 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
334 # repo_name, self.ip_addr, self.sa)
335 # repo_name, self.ip_addr, self.sa)
335 Session().commit()
336 Session().commit()
336 h.flash(_('User Group permissions updated'), category='success')
337 h.flash(_('User Group permissions updated'), category='success')
337 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
338 return redirect(url('edit_user_group_perms', user_group_id=user_group_id))
338
339
339 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
340 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
340 def edit_perms_summary(self, user_group_id):
341 def edit_perms_summary(self, user_group_id):
341 user_group_id = safe_int(user_group_id)
342 user_group_id = safe_int(user_group_id)
342 c.user_group = UserGroup.get_or_404(user_group_id)
343 c.user_group = UserGroup.get_or_404(user_group_id)
343 c.active = 'perms_summary'
344 c.active = 'perms_summary'
344 permissions = {
345 permissions = {
345 'repositories': {},
346 'repositories': {},
346 'repositories_groups': {},
347 'repositories_groups': {},
347 }
348 }
348 ugroup_repo_perms = UserGroupRepoToPerm.query()\
349 ugroup_repo_perms = UserGroupRepoToPerm.query()\
349 .options(joinedload(UserGroupRepoToPerm.permission))\
350 .options(joinedload(UserGroupRepoToPerm.permission))\
350 .options(joinedload(UserGroupRepoToPerm.repository))\
351 .options(joinedload(UserGroupRepoToPerm.repository))\
351 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
352 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
352 .all()
353 .all()
353
354
354 for gr in ugroup_repo_perms:
355 for gr in ugroup_repo_perms:
355 permissions['repositories'][gr.repository.repo_name] \
356 permissions['repositories'][gr.repository.repo_name] \
356 = gr.permission.permission_name
357 = gr.permission.permission_name
357
358
358 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
359 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
359 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
360 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
360 .options(joinedload(UserGroupRepoGroupToPerm.group))\
361 .options(joinedload(UserGroupRepoGroupToPerm.group))\
361 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
362 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
362 .all()
363 .all()
363
364
364 for gr in ugroup_group_perms:
365 for gr in ugroup_group_perms:
365 permissions['repositories_groups'][gr.group.group_name] \
366 permissions['repositories_groups'][gr.group.group_name] \
366 = gr.permission.permission_name
367 = gr.permission.permission_name
367 c.permissions = permissions
368 c.permissions = permissions
368 return render('admin/user_groups/user_group_edit.mako')
369 return render('admin/user_groups/user_group_edit.mako')
369
370
370 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
371 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
371 def edit_global_perms(self, user_group_id):
372 def edit_global_perms(self, user_group_id):
372 user_group_id = safe_int(user_group_id)
373 user_group_id = safe_int(user_group_id)
373 c.user_group = UserGroup.get_or_404(user_group_id)
374 c.user_group = UserGroup.get_or_404(user_group_id)
374 c.active = 'global_perms'
375 c.active = 'global_perms'
375
376
376 c.default_user = User.get_default_user()
377 c.default_user = User.get_default_user()
377 defaults = c.user_group.get_dict()
378 defaults = c.user_group.get_dict()
378 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
379 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
379 defaults.update(c.user_group.get_default_perms())
380 defaults.update(c.user_group.get_default_perms())
380
381
381 return htmlfill.render(
382 return htmlfill.render(
382 render('admin/user_groups/user_group_edit.mako'),
383 render('admin/user_groups/user_group_edit.mako'),
383 defaults=defaults,
384 defaults=defaults,
384 encoding="UTF-8",
385 encoding="UTF-8",
385 force_defaults=False
386 force_defaults=False
386 )
387 )
387
388
388 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
389 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
389 @auth.CSRFRequired()
390 @auth.CSRFRequired()
390 def update_global_perms(self, user_group_id):
391 def update_global_perms(self, user_group_id):
391 """PUT /users_perm/user_group_id: Update an existing item"""
392 """PUT /users_perm/user_group_id: Update an existing item"""
392 # url('users_group_perm', user_group_id=ID, method='put')
393 # url('users_group_perm', user_group_id=ID, method='put')
393 user_group_id = safe_int(user_group_id)
394 user_group_id = safe_int(user_group_id)
394 user_group = UserGroup.get_or_404(user_group_id)
395 user_group = UserGroup.get_or_404(user_group_id)
395 c.active = 'global_perms'
396 c.active = 'global_perms'
396
397
397 try:
398 try:
398 # first stage that verifies the checkbox
399 # first stage that verifies the checkbox
399 _form = UserIndividualPermissionsForm()
400 _form = UserIndividualPermissionsForm()
400 form_result = _form.to_python(dict(request.POST))
401 form_result = _form.to_python(dict(request.POST))
401 inherit_perms = form_result['inherit_default_permissions']
402 inherit_perms = form_result['inherit_default_permissions']
402 user_group.inherit_default_permissions = inherit_perms
403 user_group.inherit_default_permissions = inherit_perms
403 Session().add(user_group)
404 Session().add(user_group)
404
405
405 if not inherit_perms:
406 if not inherit_perms:
406 # only update the individual ones if we un check the flag
407 # only update the individual ones if we un check the flag
407 _form = UserPermissionsForm(
408 _form = UserPermissionsForm(
408 [x[0] for x in c.repo_create_choices],
409 [x[0] for x in c.repo_create_choices],
409 [x[0] for x in c.repo_create_on_write_choices],
410 [x[0] for x in c.repo_create_on_write_choices],
410 [x[0] for x in c.repo_group_create_choices],
411 [x[0] for x in c.repo_group_create_choices],
411 [x[0] for x in c.user_group_create_choices],
412 [x[0] for x in c.user_group_create_choices],
412 [x[0] for x in c.fork_choices],
413 [x[0] for x in c.fork_choices],
413 [x[0] for x in c.inherit_default_permission_choices])()
414 [x[0] for x in c.inherit_default_permission_choices])()
414
415
415 form_result = _form.to_python(dict(request.POST))
416 form_result = _form.to_python(dict(request.POST))
416 form_result.update({'perm_user_group_id': user_group.users_group_id})
417 form_result.update({'perm_user_group_id': user_group.users_group_id})
417
418
418 PermissionModel().update_user_group_permissions(form_result)
419 PermissionModel().update_user_group_permissions(form_result)
419
420
420 Session().commit()
421 Session().commit()
421 h.flash(_('User Group global permissions updated successfully'),
422 h.flash(_('User Group global permissions updated successfully'),
422 category='success')
423 category='success')
423
424
424 except formencode.Invalid as errors:
425 except formencode.Invalid as errors:
425 defaults = errors.value
426 defaults = errors.value
426 c.user_group = user_group
427 c.user_group = user_group
427 return htmlfill.render(
428 return htmlfill.render(
428 render('admin/user_groups/user_group_edit.mako'),
429 render('admin/user_groups/user_group_edit.mako'),
429 defaults=defaults,
430 defaults=defaults,
430 errors=errors.error_dict or {},
431 errors=errors.error_dict or {},
431 prefix_error=False,
432 prefix_error=False,
432 encoding="UTF-8",
433 encoding="UTF-8",
433 force_defaults=False)
434 force_defaults=False)
434
435 except Exception:
435 except Exception:
436 log.exception("Exception during permissions saving")
436 log.exception("Exception during permissions saving")
437 h.flash(_('An error occurred during permissions saving'),
437 h.flash(_('An error occurred during permissions saving'),
438 category='error')
438 category='error')
439
439
440 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
440 return redirect(url('edit_user_group_global_perms', user_group_id=user_group_id))
441
441
442 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
442 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
443 def edit_advanced(self, user_group_id):
443 def edit_advanced(self, user_group_id):
444 user_group_id = safe_int(user_group_id)
444 user_group_id = safe_int(user_group_id)
445 c.user_group = UserGroup.get_or_404(user_group_id)
445 c.user_group = UserGroup.get_or_404(user_group_id)
446 c.active = 'advanced'
446 c.active = 'advanced'
447 c.group_members_obj = sorted(
447 c.group_members_obj = sorted(
448 (x.user for x in c.user_group.members),
448 (x.user for x in c.user_group.members),
449 key=lambda u: u.username.lower())
449 key=lambda u: u.username.lower())
450
450
451 c.group_to_repos = sorted(
451 c.group_to_repos = sorted(
452 (x.repository for x in c.user_group.users_group_repo_to_perm),
452 (x.repository for x in c.user_group.users_group_repo_to_perm),
453 key=lambda u: u.repo_name.lower())
453 key=lambda u: u.repo_name.lower())
454
454
455 c.group_to_repo_groups = sorted(
455 c.group_to_repo_groups = sorted(
456 (x.group for x in c.user_group.users_group_repo_group_to_perm),
456 (x.group for x in c.user_group.users_group_repo_group_to_perm),
457 key=lambda u: u.group_name.lower())
457 key=lambda u: u.group_name.lower())
458
458
459 return render('admin/user_groups/user_group_edit.mako')
459 return render('admin/user_groups/user_group_edit.mako')
460
460
461 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
461 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
462 def edit_advanced_set_synchronization(self, user_group_id):
463 user_group_id = safe_int(user_group_id)
464 user_group = UserGroup.get_or_404(user_group_id)
465
466 existing = user_group.group_data.get('extern_type')
467
468 if existing:
469 new_state = user_group.group_data
470 new_state['extern_type'] = None
471 else:
472 new_state = user_group.group_data
473 new_state['extern_type'] = 'manual'
474 new_state['extern_type_set_by'] = c.rhodecode_user.username
475
476 try:
477 user_group.group_data = new_state
478 Session().add(user_group)
479 Session().commit()
480
481 h.flash(_('User Group synchronization updated successfully'),
482 category='success')
483 except Exception:
484 log.exception("Exception during sync settings saving")
485 h.flash(_('An error occurred during synchronization update'),
486 category='error')
487
488 return redirect(
489 url('edit_user_group_advanced', user_group_id=user_group_id))
490
491 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
462 @XHRRequired()
492 @XHRRequired()
463 @jsonify
493 @jsonify
464 def user_group_members(self, user_group_id):
494 def user_group_members(self, user_group_id):
465 user_group_id = safe_int(user_group_id)
495 user_group_id = safe_int(user_group_id)
466 user_group = UserGroup.get_or_404(user_group_id)
496 user_group = UserGroup.get_or_404(user_group_id)
467 group_members_obj = sorted((x.user for x in user_group.members),
497 group_members_obj = sorted((x.user for x in user_group.members),
468 key=lambda u: u.username.lower())
498 key=lambda u: u.username.lower())
469
499
470 group_members = [
500 group_members = [
471 {
501 {
472 'id': user.user_id,
502 'id': user.user_id,
473 'first_name': user.name,
503 'first_name': user.name,
474 'last_name': user.lastname,
504 'last_name': user.lastname,
475 'username': user.username,
505 'username': user.username,
476 'icon_link': h.gravatar_url(user.email, 30),
506 'icon_link': h.gravatar_url(user.email, 30),
477 'value_display': h.person(user.email),
507 'value_display': h.person(user.email),
478 'value': user.username,
508 'value': user.username,
479 'value_type': 'user',
509 'value_type': 'user',
480 'active': user.active,
510 'active': user.active,
481 }
511 }
482 for user in group_members_obj
512 for user in group_members_obj
483 ]
513 ]
484
514
485 return {
515 return {
486 'members': group_members
516 'members': group_members
487 }
517 }
@@ -1,560 +1,559 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-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 """
22 """
23 user group model for RhodeCode
23 user group model for RhodeCode
24 """
24 """
25
25
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.lib.utils2 import safe_str
30 from rhodecode.lib.utils2 import safe_str
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import UserGroupMember, UserGroup,\
32 from rhodecode.model.db import UserGroupMember, UserGroup,\
33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
36 RepoGroupAssignmentError
36 RepoGroupAssignmentError
37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class UserGroupModel(BaseModel):
42 class UserGroupModel(BaseModel):
43
43
44 cls = UserGroup
44 cls = UserGroup
45
45
46 def _get_user_group(self, user_group):
46 def _get_user_group(self, user_group):
47 return self._get_instance(UserGroup, user_group,
47 return self._get_instance(UserGroup, user_group,
48 callback=UserGroup.get_by_group_name)
48 callback=UserGroup.get_by_group_name)
49
49
50 def _create_default_perms(self, user_group):
50 def _create_default_perms(self, user_group):
51 # create default permission
51 # create default permission
52 default_perm = 'usergroup.read'
52 default_perm = 'usergroup.read'
53 def_user = User.get_default_user()
53 def_user = User.get_default_user()
54 for p in def_user.user_perms:
54 for p in def_user.user_perms:
55 if p.permission.permission_name.startswith('usergroup.'):
55 if p.permission.permission_name.startswith('usergroup.'):
56 default_perm = p.permission.permission_name
56 default_perm = p.permission.permission_name
57 break
57 break
58
58
59 user_group_to_perm = UserUserGroupToPerm()
59 user_group_to_perm = UserUserGroupToPerm()
60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
61
61
62 user_group_to_perm.user_group = user_group
62 user_group_to_perm.user_group = user_group
63 user_group_to_perm.user_id = def_user.user_id
63 user_group_to_perm.user_id = def_user.user_id
64 return user_group_to_perm
64 return user_group_to_perm
65
65
66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
67 perm_deletions=None, check_perms=True, cur_user=None):
67 perm_deletions=None, check_perms=True, cur_user=None):
68 from rhodecode.lib.auth import HasUserGroupPermissionAny
68 from rhodecode.lib.auth import HasUserGroupPermissionAny
69 if not perm_additions:
69 if not perm_additions:
70 perm_additions = []
70 perm_additions = []
71 if not perm_updates:
71 if not perm_updates:
72 perm_updates = []
72 perm_updates = []
73 if not perm_deletions:
73 if not perm_deletions:
74 perm_deletions = []
74 perm_deletions = []
75
75
76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
77
77
78 # update permissions
78 # update permissions
79 for member_id, perm, member_type in perm_updates:
79 for member_id, perm, member_type in perm_updates:
80 member_id = int(member_id)
80 member_id = int(member_id)
81 if member_type == 'user':
81 if member_type == 'user':
82 # this updates existing one
82 # this updates existing one
83 self.grant_user_permission(
83 self.grant_user_permission(
84 user_group=user_group, user=member_id, perm=perm
84 user_group=user_group, user=member_id, perm=perm
85 )
85 )
86 else:
86 else:
87 # check if we have permissions to alter this usergroup
87 # check if we have permissions to alter this usergroup
88 member_name = UserGroup.get(member_id).users_group_name
88 member_name = UserGroup.get(member_id).users_group_name
89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
90 self.grant_user_group_permission(
90 self.grant_user_group_permission(
91 target_user_group=user_group, user_group=member_id, perm=perm
91 target_user_group=user_group, user_group=member_id, perm=perm
92 )
92 )
93
93
94 # set new permissions
94 # set new permissions
95 for member_id, perm, member_type in perm_additions:
95 for member_id, perm, member_type in perm_additions:
96 member_id = int(member_id)
96 member_id = int(member_id)
97 if member_type == 'user':
97 if member_type == 'user':
98 self.grant_user_permission(
98 self.grant_user_permission(
99 user_group=user_group, user=member_id, perm=perm
99 user_group=user_group, user=member_id, perm=perm
100 )
100 )
101 else:
101 else:
102 # check if we have permissions to alter this usergroup
102 # check if we have permissions to alter this usergroup
103 member_name = UserGroup.get(member_id).users_group_name
103 member_name = UserGroup.get(member_id).users_group_name
104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
105 self.grant_user_group_permission(
105 self.grant_user_group_permission(
106 target_user_group=user_group, user_group=member_id, perm=perm
106 target_user_group=user_group, user_group=member_id, perm=perm
107 )
107 )
108
108
109 # delete permissions
109 # delete permissions
110 for member_id, perm, member_type in perm_deletions:
110 for member_id, perm, member_type in perm_deletions:
111 member_id = int(member_id)
111 member_id = int(member_id)
112 if member_type == 'user':
112 if member_type == 'user':
113 self.revoke_user_permission(user_group=user_group, user=member_id)
113 self.revoke_user_permission(user_group=user_group, user=member_id)
114 else:
114 else:
115 #check if we have permissions to alter this usergroup
115 #check if we have permissions to alter this usergroup
116 member_name = UserGroup.get(member_id).users_group_name
116 member_name = UserGroup.get(member_id).users_group_name
117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
118 self.revoke_user_group_permission(
118 self.revoke_user_group_permission(
119 target_user_group=user_group, user_group=member_id
119 target_user_group=user_group, user_group=member_id
120 )
120 )
121
121
122 def get(self, user_group_id, cache=False):
122 def get(self, user_group_id, cache=False):
123 return UserGroup.get(user_group_id)
123 return UserGroup.get(user_group_id)
124
124
125 def get_group(self, user_group):
125 def get_group(self, user_group):
126 return self._get_user_group(user_group)
126 return self._get_user_group(user_group)
127
127
128 def get_by_name(self, name, cache=False, case_insensitive=False):
128 def get_by_name(self, name, cache=False, case_insensitive=False):
129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
130
130
131 def create(self, name, description, owner, active=True, group_data=None):
131 def create(self, name, description, owner, active=True, group_data=None):
132 try:
132 try:
133 new_user_group = UserGroup()
133 new_user_group = UserGroup()
134 new_user_group.user = self._get_user(owner)
134 new_user_group.user = self._get_user(owner)
135 new_user_group.users_group_name = name
135 new_user_group.users_group_name = name
136 new_user_group.user_group_description = description
136 new_user_group.user_group_description = description
137 new_user_group.users_group_active = active
137 new_user_group.users_group_active = active
138 if group_data:
138 if group_data:
139 new_user_group.group_data = group_data
139 new_user_group.group_data = group_data
140 self.sa.add(new_user_group)
140 self.sa.add(new_user_group)
141 perm_obj = self._create_default_perms(new_user_group)
141 perm_obj = self._create_default_perms(new_user_group)
142 self.sa.add(perm_obj)
142 self.sa.add(perm_obj)
143
143
144 self.grant_user_permission(user_group=new_user_group,
144 self.grant_user_permission(user_group=new_user_group,
145 user=owner, perm='usergroup.admin')
145 user=owner, perm='usergroup.admin')
146
146
147 return new_user_group
147 return new_user_group
148 except Exception:
148 except Exception:
149 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
150 raise
150 raise
151
151
152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
153 members = []
153 members = []
154 for user_id in user_id_list:
154 for user_id in user_id_list:
155 member = self._get_membership(user_group.users_group_id, user_id)
155 member = self._get_membership(user_group.users_group_id, user_id)
156 members.append(member)
156 members.append(member)
157 return members
157 return members
158
158
159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
160 current_members = user_group.members or []
160 current_members = user_group.members or []
161 current_members_ids = [m.user.user_id for m in current_members]
161 current_members_ids = [m.user.user_id for m in current_members]
162
162
163 added_members = [
163 added_members = [
164 user_id for user_id in user_id_list
164 user_id for user_id in user_id_list
165 if user_id not in current_members_ids]
165 if user_id not in current_members_ids]
166 if user_id_list == []:
166 if user_id_list == []:
167 # all members were deleted
167 # all members were deleted
168 deleted_members = current_members_ids
168 deleted_members = current_members_ids
169 else:
169 else:
170 deleted_members = [
170 deleted_members = [
171 user_id for user_id in current_members_ids
171 user_id for user_id in current_members_ids
172 if user_id not in user_id_list]
172 if user_id not in user_id_list]
173
173
174 return (added_members, deleted_members)
174 return (added_members, deleted_members)
175
175
176 def _set_users_as_members(self, user_group, user_ids):
176 def _set_users_as_members(self, user_group, user_ids):
177 user_group.members = []
177 user_group.members = []
178 self.sa.flush()
178 self.sa.flush()
179 members = self._get_memberships_for_user_ids(
179 members = self._get_memberships_for_user_ids(
180 user_group, user_ids)
180 user_group, user_ids)
181 user_group.members = members
181 user_group.members = members
182 self.sa.add(user_group)
182 self.sa.add(user_group)
183
183
184 def _update_members_from_user_ids(self, user_group, user_ids):
184 def _update_members_from_user_ids(self, user_group, user_ids):
185 added, removed = self._get_added_and_removed_user_ids(
185 added, removed = self._get_added_and_removed_user_ids(
186 user_group, user_ids)
186 user_group, user_ids)
187 self._set_users_as_members(user_group, user_ids)
187 self._set_users_as_members(user_group, user_ids)
188 self._log_user_changes('added to', user_group, added)
188 self._log_user_changes('added to', user_group, added)
189 self._log_user_changes('removed from', user_group, removed)
189 self._log_user_changes('removed from', user_group, removed)
190
190
191 def _clean_members_data(self, members_data):
191 def _clean_members_data(self, members_data):
192 if not members_data:
192 if not members_data:
193 members_data = []
193 members_data = []
194
194
195 members = []
195 members = []
196 for user in members_data:
196 for user in members_data:
197 uid = int(user['member_user_id'])
197 uid = int(user['member_user_id'])
198 if uid not in members and user['type'] in ['new', 'existing']:
198 if uid not in members and user['type'] in ['new', 'existing']:
199 members.append(uid)
199 members.append(uid)
200 return members
200 return members
201
201
202 def update(self, user_group, form_data):
202 def update(self, user_group, form_data):
203 user_group = self._get_user_group(user_group)
203 user_group = self._get_user_group(user_group)
204 if 'users_group_name' in form_data:
204 if 'users_group_name' in form_data:
205 user_group.users_group_name = form_data['users_group_name']
205 user_group.users_group_name = form_data['users_group_name']
206 if 'users_group_active' in form_data:
206 if 'users_group_active' in form_data:
207 user_group.users_group_active = form_data['users_group_active']
207 user_group.users_group_active = form_data['users_group_active']
208 if 'user_group_description' in form_data:
208 if 'user_group_description' in form_data:
209 user_group.user_group_description = form_data[
209 user_group.user_group_description = form_data[
210 'user_group_description']
210 'user_group_description']
211
211
212 # handle owner change
212 # handle owner change
213 if 'user' in form_data:
213 if 'user' in form_data:
214 owner = form_data['user']
214 owner = form_data['user']
215 if isinstance(owner, basestring):
215 if isinstance(owner, basestring):
216 owner = User.get_by_username(form_data['user'])
216 owner = User.get_by_username(form_data['user'])
217
217
218 if not isinstance(owner, User):
218 if not isinstance(owner, User):
219 raise ValueError(
219 raise ValueError(
220 'invalid owner for user group: %s' % form_data['user'])
220 'invalid owner for user group: %s' % form_data['user'])
221
221
222 user_group.user = owner
222 user_group.user = owner
223
223
224 if 'users_group_members' in form_data:
224 if 'users_group_members' in form_data:
225 members_id_list = self._clean_members_data(
225 members_id_list = self._clean_members_data(
226 form_data['users_group_members'])
226 form_data['users_group_members'])
227 self._update_members_from_user_ids(user_group, members_id_list)
227 self._update_members_from_user_ids(user_group, members_id_list)
228
228
229 self.sa.add(user_group)
229 self.sa.add(user_group)
230
230
231 def delete(self, user_group, force=False):
231 def delete(self, user_group, force=False):
232 """
232 """
233 Deletes repository group, unless force flag is used
233 Deletes repository group, unless force flag is used
234 raises exception if there are members in that group, else deletes
234 raises exception if there are members in that group, else deletes
235 group and users
235 group and users
236
236
237 :param user_group:
237 :param user_group:
238 :param force:
238 :param force:
239 """
239 """
240 user_group = self._get_user_group(user_group)
240 user_group = self._get_user_group(user_group)
241 try:
241 try:
242 # check if this group is not assigned to repo
242 # check if this group is not assigned to repo
243 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
243 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
244 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
244 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
245 # check if this group is not assigned to repo
245 # check if this group is not assigned to repo
246 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
246 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
247 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
247 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
248
248
249 if (assigned_to_repo or assigned_to_repo_group) and not force:
249 if (assigned_to_repo or assigned_to_repo_group) and not force:
250 assigned = ','.join(map(safe_str,
250 assigned = ','.join(map(safe_str,
251 assigned_to_repo+assigned_to_repo_group))
251 assigned_to_repo+assigned_to_repo_group))
252
252
253 raise UserGroupAssignedException(
253 raise UserGroupAssignedException(
254 'UserGroup assigned to %s' % (assigned,))
254 'UserGroup assigned to %s' % (assigned,))
255 self.sa.delete(user_group)
255 self.sa.delete(user_group)
256 except Exception:
256 except Exception:
257 log.error(traceback.format_exc())
257 log.error(traceback.format_exc())
258 raise
258 raise
259
259
260 def _log_user_changes(self, action, user_group, user_or_users):
260 def _log_user_changes(self, action, user_group, user_or_users):
261 users = user_or_users
261 users = user_or_users
262 if not isinstance(users, (list, tuple)):
262 if not isinstance(users, (list, tuple)):
263 users = [users]
263 users = [users]
264 rhodecode_user = get_current_rhodecode_user()
264 rhodecode_user = get_current_rhodecode_user()
265 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
265 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
266 group_name = user_group.users_group_name
266 group_name = user_group.users_group_name
267
267
268 for user_or_user_id in users:
268 for user_or_user_id in users:
269 user = self._get_user(user_or_user_id)
269 user = self._get_user(user_or_user_id)
270 log_text = 'User {user} {action} {group}'.format(
270 log_text = 'User {user} {action} {group}'.format(
271 action=action, user=user.username, group=group_name)
271 action=action, user=user.username, group=group_name)
272 log.info('Logging action: {0} by {1} ip:{2}'.format(
272 log.info('Logging action: {0} by {1} ip:{2}'.format(
273 log_text, rhodecode_user, ipaddr))
273 log_text, rhodecode_user, ipaddr))
274
274
275 def _find_user_in_group(self, user, user_group):
275 def _find_user_in_group(self, user, user_group):
276 user_group_member = None
276 user_group_member = None
277 for m in user_group.members:
277 for m in user_group.members:
278 if m.user_id == user.user_id:
278 if m.user_id == user.user_id:
279 # Found this user's membership row
279 # Found this user's membership row
280 user_group_member = m
280 user_group_member = m
281 break
281 break
282
282
283 return user_group_member
283 return user_group_member
284
284
285 def _get_membership(self, user_group_id, user_id):
285 def _get_membership(self, user_group_id, user_id):
286 user_group_member = UserGroupMember(user_group_id, user_id)
286 user_group_member = UserGroupMember(user_group_id, user_id)
287 return user_group_member
287 return user_group_member
288
288
289 def add_user_to_group(self, user_group, user):
289 def add_user_to_group(self, user_group, user):
290 user_group = self._get_user_group(user_group)
290 user_group = self._get_user_group(user_group)
291 user = self._get_user(user)
291 user = self._get_user(user)
292 user_member = self._find_user_in_group(user, user_group)
292 user_member = self._find_user_in_group(user, user_group)
293 if user_member:
293 if user_member:
294 # user already in the group, skip
294 # user already in the group, skip
295 return True
295 return True
296
296
297 member = self._get_membership(
297 member = self._get_membership(
298 user_group.users_group_id, user.user_id)
298 user_group.users_group_id, user.user_id)
299 user_group.members.append(member)
299 user_group.members.append(member)
300
300
301 try:
301 try:
302 self.sa.add(member)
302 self.sa.add(member)
303 except Exception:
303 except Exception:
304 # what could go wrong here?
304 # what could go wrong here?
305 log.error(traceback.format_exc())
305 log.error(traceback.format_exc())
306 raise
306 raise
307
307
308 self._log_user_changes('added to', user_group, user)
308 self._log_user_changes('added to', user_group, user)
309 return member
309 return member
310
310
311 def remove_user_from_group(self, user_group, user):
311 def remove_user_from_group(self, user_group, user):
312 user_group = self._get_user_group(user_group)
312 user_group = self._get_user_group(user_group)
313 user = self._get_user(user)
313 user = self._get_user(user)
314 user_group_member = self._find_user_in_group(user, user_group)
314 user_group_member = self._find_user_in_group(user, user_group)
315
315
316 if not user_group_member:
316 if not user_group_member:
317 # User isn't in that group
317 # User isn't in that group
318 return False
318 return False
319
319
320 try:
320 try:
321 self.sa.delete(user_group_member)
321 self.sa.delete(user_group_member)
322 except Exception:
322 except Exception:
323 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
324 raise
324 raise
325
325
326 self._log_user_changes('removed from', user_group, user)
326 self._log_user_changes('removed from', user_group, user)
327 return True
327 return True
328
328
329 def has_perm(self, user_group, perm):
329 def has_perm(self, user_group, perm):
330 user_group = self._get_user_group(user_group)
330 user_group = self._get_user_group(user_group)
331 perm = self._get_perm(perm)
331 perm = self._get_perm(perm)
332
332
333 return UserGroupToPerm.query()\
333 return UserGroupToPerm.query()\
334 .filter(UserGroupToPerm.users_group == user_group)\
334 .filter(UserGroupToPerm.users_group == user_group)\
335 .filter(UserGroupToPerm.permission == perm).scalar() is not None
335 .filter(UserGroupToPerm.permission == perm).scalar() is not None
336
336
337 def grant_perm(self, user_group, perm):
337 def grant_perm(self, user_group, perm):
338 user_group = self._get_user_group(user_group)
338 user_group = self._get_user_group(user_group)
339 perm = self._get_perm(perm)
339 perm = self._get_perm(perm)
340
340
341 # if this permission is already granted skip it
341 # if this permission is already granted skip it
342 _perm = UserGroupToPerm.query()\
342 _perm = UserGroupToPerm.query()\
343 .filter(UserGroupToPerm.users_group == user_group)\
343 .filter(UserGroupToPerm.users_group == user_group)\
344 .filter(UserGroupToPerm.permission == perm)\
344 .filter(UserGroupToPerm.permission == perm)\
345 .scalar()
345 .scalar()
346 if _perm:
346 if _perm:
347 return
347 return
348
348
349 new = UserGroupToPerm()
349 new = UserGroupToPerm()
350 new.users_group = user_group
350 new.users_group = user_group
351 new.permission = perm
351 new.permission = perm
352 self.sa.add(new)
352 self.sa.add(new)
353 return new
353 return new
354
354
355 def revoke_perm(self, user_group, perm):
355 def revoke_perm(self, user_group, perm):
356 user_group = self._get_user_group(user_group)
356 user_group = self._get_user_group(user_group)
357 perm = self._get_perm(perm)
357 perm = self._get_perm(perm)
358
358
359 obj = UserGroupToPerm.query()\
359 obj = UserGroupToPerm.query()\
360 .filter(UserGroupToPerm.users_group == user_group)\
360 .filter(UserGroupToPerm.users_group == user_group)\
361 .filter(UserGroupToPerm.permission == perm).scalar()
361 .filter(UserGroupToPerm.permission == perm).scalar()
362 if obj:
362 if obj:
363 self.sa.delete(obj)
363 self.sa.delete(obj)
364
364
365 def grant_user_permission(self, user_group, user, perm):
365 def grant_user_permission(self, user_group, user, perm):
366 """
366 """
367 Grant permission for user on given user group, or update
367 Grant permission for user on given user group, or update
368 existing one if found
368 existing one if found
369
369
370 :param user_group: Instance of UserGroup, users_group_id,
370 :param user_group: Instance of UserGroup, users_group_id,
371 or users_group_name
371 or users_group_name
372 :param user: Instance of User, user_id or username
372 :param user: Instance of User, user_id or username
373 :param perm: Instance of Permission, or permission_name
373 :param perm: Instance of Permission, or permission_name
374 """
374 """
375
375
376 user_group = self._get_user_group(user_group)
376 user_group = self._get_user_group(user_group)
377 user = self._get_user(user)
377 user = self._get_user(user)
378 permission = self._get_perm(perm)
378 permission = self._get_perm(perm)
379
379
380 # check if we have that permission already
380 # check if we have that permission already
381 obj = self.sa.query(UserUserGroupToPerm)\
381 obj = self.sa.query(UserUserGroupToPerm)\
382 .filter(UserUserGroupToPerm.user == user)\
382 .filter(UserUserGroupToPerm.user == user)\
383 .filter(UserUserGroupToPerm.user_group == user_group)\
383 .filter(UserUserGroupToPerm.user_group == user_group)\
384 .scalar()
384 .scalar()
385 if obj is None:
385 if obj is None:
386 # create new !
386 # create new !
387 obj = UserUserGroupToPerm()
387 obj = UserUserGroupToPerm()
388 obj.user_group = user_group
388 obj.user_group = user_group
389 obj.user = user
389 obj.user = user
390 obj.permission = permission
390 obj.permission = permission
391 self.sa.add(obj)
391 self.sa.add(obj)
392 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
392 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
393 action_logger_generic(
393 action_logger_generic(
394 'granted permission: {} to user: {} on usergroup: {}'.format(
394 'granted permission: {} to user: {} on usergroup: {}'.format(
395 perm, user, user_group), namespace='security.usergroup')
395 perm, user, user_group), namespace='security.usergroup')
396
396
397 return obj
397 return obj
398
398
399 def revoke_user_permission(self, user_group, user):
399 def revoke_user_permission(self, user_group, user):
400 """
400 """
401 Revoke permission for user on given user group
401 Revoke permission for user on given user group
402
402
403 :param user_group: Instance of UserGroup, users_group_id,
403 :param user_group: Instance of UserGroup, users_group_id,
404 or users_group name
404 or users_group name
405 :param user: Instance of User, user_id or username
405 :param user: Instance of User, user_id or username
406 """
406 """
407
407
408 user_group = self._get_user_group(user_group)
408 user_group = self._get_user_group(user_group)
409 user = self._get_user(user)
409 user = self._get_user(user)
410
410
411 obj = self.sa.query(UserUserGroupToPerm)\
411 obj = self.sa.query(UserUserGroupToPerm)\
412 .filter(UserUserGroupToPerm.user == user)\
412 .filter(UserUserGroupToPerm.user == user)\
413 .filter(UserUserGroupToPerm.user_group == user_group)\
413 .filter(UserUserGroupToPerm.user_group == user_group)\
414 .scalar()
414 .scalar()
415 if obj:
415 if obj:
416 self.sa.delete(obj)
416 self.sa.delete(obj)
417 log.debug('Revoked perm on %s on %s', user_group, user)
417 log.debug('Revoked perm on %s on %s', user_group, user)
418 action_logger_generic(
418 action_logger_generic(
419 'revoked permission from user: {} on usergroup: {}'.format(
419 'revoked permission from user: {} on usergroup: {}'.format(
420 user, user_group), namespace='security.usergroup')
420 user, user_group), namespace='security.usergroup')
421
421
422 def grant_user_group_permission(self, target_user_group, user_group, perm):
422 def grant_user_group_permission(self, target_user_group, user_group, perm):
423 """
423 """
424 Grant user group permission for given target_user_group
424 Grant user group permission for given target_user_group
425
425
426 :param target_user_group:
426 :param target_user_group:
427 :param user_group:
427 :param user_group:
428 :param perm:
428 :param perm:
429 """
429 """
430 target_user_group = self._get_user_group(target_user_group)
430 target_user_group = self._get_user_group(target_user_group)
431 user_group = self._get_user_group(user_group)
431 user_group = self._get_user_group(user_group)
432 permission = self._get_perm(perm)
432 permission = self._get_perm(perm)
433 # forbid assigning same user group to itself
433 # forbid assigning same user group to itself
434 if target_user_group == user_group:
434 if target_user_group == user_group:
435 raise RepoGroupAssignmentError('target repo:%s cannot be '
435 raise RepoGroupAssignmentError('target repo:%s cannot be '
436 'assigned to itself' % target_user_group)
436 'assigned to itself' % target_user_group)
437
437
438 # check if we have that permission already
438 # check if we have that permission already
439 obj = self.sa.query(UserGroupUserGroupToPerm)\
439 obj = self.sa.query(UserGroupUserGroupToPerm)\
440 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
440 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
441 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
441 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
442 .scalar()
442 .scalar()
443 if obj is None:
443 if obj is None:
444 # create new !
444 # create new !
445 obj = UserGroupUserGroupToPerm()
445 obj = UserGroupUserGroupToPerm()
446 obj.user_group = user_group
446 obj.user_group = user_group
447 obj.target_user_group = target_user_group
447 obj.target_user_group = target_user_group
448 obj.permission = permission
448 obj.permission = permission
449 self.sa.add(obj)
449 self.sa.add(obj)
450 log.debug(
450 log.debug(
451 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
451 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
452 action_logger_generic(
452 action_logger_generic(
453 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
453 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
454 perm, user_group, target_user_group),
454 perm, user_group, target_user_group),
455 namespace='security.usergroup')
455 namespace='security.usergroup')
456
456
457 return obj
457 return obj
458
458
459 def revoke_user_group_permission(self, target_user_group, user_group):
459 def revoke_user_group_permission(self, target_user_group, user_group):
460 """
460 """
461 Revoke user group permission for given target_user_group
461 Revoke user group permission for given target_user_group
462
462
463 :param target_user_group:
463 :param target_user_group:
464 :param user_group:
464 :param user_group:
465 """
465 """
466 target_user_group = self._get_user_group(target_user_group)
466 target_user_group = self._get_user_group(target_user_group)
467 user_group = self._get_user_group(user_group)
467 user_group = self._get_user_group(user_group)
468
468
469 obj = self.sa.query(UserGroupUserGroupToPerm)\
469 obj = self.sa.query(UserGroupUserGroupToPerm)\
470 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
470 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
471 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
471 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
472 .scalar()
472 .scalar()
473 if obj:
473 if obj:
474 self.sa.delete(obj)
474 self.sa.delete(obj)
475 log.debug(
475 log.debug(
476 'Revoked perm on %s on %s', target_user_group, user_group)
476 'Revoked perm on %s on %s', target_user_group, user_group)
477 action_logger_generic(
477 action_logger_generic(
478 'revoked permission from usergroup: {} on usergroup: {}'.format(
478 'revoked permission from usergroup: {} on usergroup: {}'.format(
479 user_group, target_user_group),
479 user_group, target_user_group),
480 namespace='security.repogroup')
480 namespace='security.repogroup')
481
481
482 def enforce_groups(self, user, groups, extern_type=None):
482 def enforce_groups(self, user, groups, extern_type=None):
483 user = self._get_user(user)
483 user = self._get_user(user)
484 log.debug('Enforcing groups %s on user %s', groups, user)
484 log.debug('Enforcing groups %s on user %s', groups, user)
485 current_groups = user.group_member
485 current_groups = user.group_member
486 # find the external created groups
486 # find the external created groups
487 externals = [x.users_group for x in current_groups
487 externals = [x.users_group for x in current_groups
488 if 'extern_type' in x.users_group.group_data]
488 if 'extern_type' in x.users_group.group_data]
489
489
490 # calculate from what groups user should be removed
490 # calculate from what groups user should be removed
491 # externals that are not in groups
491 # externals that are not in groups
492 for gr in externals:
492 for gr in externals:
493 if gr.users_group_name not in groups:
493 if gr.users_group_name not in groups:
494 log.debug('Removing user %s from user group %s', user, gr)
494 log.debug('Removing user %s from user group %s', user, gr)
495 self.remove_user_from_group(gr, user)
495 self.remove_user_from_group(gr, user)
496
496
497 # now we calculate in which groups user should be == groups params
497 # now we calculate in which groups user should be == groups params
498 owner = User.get_first_super_admin().username
498 owner = User.get_first_super_admin().username
499 for gr in set(groups):
499 for gr in set(groups):
500 existing_group = UserGroup.get_by_group_name(gr)
500 existing_group = UserGroup.get_by_group_name(gr)
501 if not existing_group:
501 if not existing_group:
502 desc = 'Automatically created from plugin:%s' % extern_type
502 desc = 'Automatically created from plugin:%s' % extern_type
503 # we use first admin account to set the owner of the group
503 # we use first admin account to set the owner of the group
504 existing_group = UserGroupModel().create(gr, desc, owner,
504 existing_group = UserGroupModel().create(
505 group_data={'extern_type': extern_type})
505 gr, desc, owner, group_data={'extern_type': extern_type})
506
506
507 # we can only add users to special groups created via plugins
507 # we can only add users to special groups created via plugins
508 managed = 'extern_type' in existing_group.group_data
508 managed = 'extern_type' in existing_group.group_data
509 if managed:
509 if managed:
510 log.debug('Adding user %s to user group %s', user, gr)
510 log.debug('Adding user %s to user group %s', user, gr)
511 UserGroupModel().add_user_to_group(existing_group, user)
511 UserGroupModel().add_user_to_group(existing_group, user)
512 else:
512 else:
513 log.debug('Skipping addition to group %s since it is '
513 log.debug('Skipping addition to group %s since it is '
514 'not managed by auth plugins' % gr)
514 'not set to be automatically synchronized' % gr)
515
516
515
517 def change_groups(self, user, groups):
516 def change_groups(self, user, groups):
518 """
517 """
519 This method changes user group assignment
518 This method changes user group assignment
520 :param user: User
519 :param user: User
521 :param groups: array of UserGroupModel
520 :param groups: array of UserGroupModel
522 :return:
521 :return:
523 """
522 """
524 user = self._get_user(user)
523 user = self._get_user(user)
525 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
524 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
526 current_groups = user.group_member
525 current_groups = user.group_member
527 current_groups = [x.users_group for x in current_groups]
526 current_groups = [x.users_group for x in current_groups]
528
527
529 # calculate from what groups user should be removed/add
528 # calculate from what groups user should be removed/add
530 groups = set(groups)
529 groups = set(groups)
531 current_groups = set(current_groups)
530 current_groups = set(current_groups)
532
531
533 groups_to_remove = current_groups - groups
532 groups_to_remove = current_groups - groups
534 groups_to_add = groups - current_groups
533 groups_to_add = groups - current_groups
535
534
536 for gr in groups_to_remove:
535 for gr in groups_to_remove:
537 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
536 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
538 self.remove_user_from_group(gr.users_group_name, user.username)
537 self.remove_user_from_group(gr.users_group_name, user.username)
539 for gr in groups_to_add:
538 for gr in groups_to_add:
540 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
539 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
541 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
540 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
542
541
543 @staticmethod
542 @staticmethod
544 def get_user_groups_as_dict(user_group):
543 def get_user_groups_as_dict(user_group):
545 import rhodecode.lib.helpers as h
544 import rhodecode.lib.helpers as h
546
545
547 data = {
546 data = {
548 'users_group_id': user_group.users_group_id,
547 'users_group_id': user_group.users_group_id,
549 'group_name': user_group.users_group_name,
548 'group_name': user_group.users_group_name,
550 'group_description': user_group.user_group_description,
549 'group_description': user_group.user_group_description,
551 'active': user_group.users_group_active,
550 'active': user_group.users_group_active,
552 "owner": user_group.user.username,
551 "owner": user_group.user.username,
553 'owner_icon': h.gravatar_url(user_group.user.email, 30),
552 'owner_icon': h.gravatar_url(user_group.user.email, 30),
554 "owner_data": {'owner': user_group.user.username, 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
553 "owner_data": {'owner': user_group.user.username, 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
555 }
554 }
556 return data
555 return data
557
556
558
557
559
558
560
559
@@ -1,38 +1,87 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <%
3 <%
4 elems = [
4 elems = [
5 (_('Owner'), lambda:base.gravatar_with_user(c.user_group.user.email), '', ''),
5 (_('Owner'), lambda:base.gravatar_with_user(c.user_group.user.email), '', ''),
6 (_('Created on'), h.format_date(c.user_group.created_on), '', '',),
6 (_('Created on'), h.format_date(c.user_group.created_on), '', '',),
7
7
8 (_('Members'), len(c.group_members_obj),'', [x for x in c.group_members_obj]),
8 (_('Members'), len(c.group_members_obj),'', [x for x in c.group_members_obj]),
9 (_('Automatic member sync'), 'Yes' if c.user_group.group_data.get('extern_type') else 'No', '', '',),
10
9 (_('Assigned to repositories'), len(c.group_to_repos),'', [x for x in c.group_to_repos]),
11 (_('Assigned to repositories'), len(c.group_to_repos),'', [x for x in c.group_to_repos]),
10 (_('Assigned to repo groups'), len(c.group_to_repo_groups), '', [x for x in c.group_to_repo_groups]),
12 (_('Assigned to repo groups'), len(c.group_to_repo_groups), '', [x for x in c.group_to_repo_groups]),
11
13
12 ]
14 ]
13 %>
15 %>
14
16
15 <div class="panel panel-default">
17 <div class="panel panel-default">
16 <div class="panel-heading">
18 <div class="panel-heading">
17 <h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
19 <h3 class="panel-title">${_('User Group: %s') % c.user_group.users_group_name}</h3>
18 </div>
20 </div>
19 <div class="panel-body">
21 <div class="panel-body">
20 ${base.dt_info_panel(elems)}
22 ${base.dt_info_panel(elems)}
21 </div>
23 </div>
24
22 </div>
25 </div>
23
26
27 <div class="panel panel-default">
28 <div class="panel-heading">
29 <h3 class="panel-title">${_('Group members sync')}</h3>
30 </div>
31 <div class="panel-body">
32 <% sync_type = c.user_group.group_data.get('extern_type') %>
33
34 % if sync_type:
35 <p>
36 ${_('This group is set to be automatically synchronised.')}<br/>
37 ${_('Each member will be added or removed from this groups once they interact with RhodeCode system.')}<br/>
38 ${_('This group synchronization was set by')}: <strong>${sync_type}</strong>
39 </p>
40 % else:
41 <p>
42 ${_('This group is not set to be automatically synchronised')}
43 </p>
44 % endif
45
46 <div>
47 ${h.secure_form(h.url('edit_user_group_advanced_sync', user_group_id=c.user_group.users_group_id), method='post')}
48 <div class="field">
49 <button class="btn btn-default" type="submit">
50 %if sync_type:
51 ${_('Disable synchronization')}
52 %else:
53 ${_('Enable synchronization')}
54 %endif
55 </button>
56 </div>
57 <div class="field">
58 <span class="help-block">
59 %if sync_type:
60 ${_('User group will no longer synchronize membership')}
61 %else:
62 ${_('User group will start to synchronize membership')}
63 %endif
64 </span>
65 </div>
66 ${h.end_form()}
67 </div>
68
69 </div>
70 </div>
71
72
24 <div class="panel panel-danger">
73 <div class="panel panel-danger">
25 <div class="panel-heading">
74 <div class="panel-heading">
26 <h3 class="panel-title">${_('Delete User Group')}</h3>
75 <h3 class="panel-title">${_('Delete User Group')}</h3>
27 </div>
76 </div>
28 <div class="panel-body">
77 <div class="panel-body">
29 ${h.secure_form(h.url('delete_users_group', user_group_id=c.user_group.users_group_id),method='delete')}
78 ${h.secure_form(h.url('delete_users_group', user_group_id=c.user_group.users_group_id),method='delete')}
30 ${h.hidden('force', 1)}
79 ${h.hidden('force', 1)}
31 <button class="btn btn-small btn-danger" type="submit"
80 <button class="btn btn-small btn-danger" type="submit"
32 onclick="return confirm('${_('Confirm to delete user group `%(ugroup)s` with all permission assignments') % {'ugroup': c.user_group.users_group_name}}');">
81 onclick="return confirm('${_('Confirm to delete user group `%(ugroup)s` with all permission assignments') % {'ugroup': c.user_group.users_group_name}}');">
33 <i class="icon-remove-sign"></i>
82 <i class="icon-remove-sign"></i>
34 ${_('Delete This User Group')}
83 ${_('Delete This User Group')}
35 </button>
84 </button>
36 ${h.end_form()}
85 ${h.end_form()}
37 </div>
86 </div>
38 </div>
87 </div>
@@ -1,98 +1,100 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 ${_('User groups administration')}
5 ${_('User groups administration')}
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 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22
22
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 <ul class="links">
25 <ul class="links">
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
27 <li>
27 <li>
28 <a href="${h.url('new_users_group')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
28 <a href="${h.url('new_users_group')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
29 </li>
29 </li>
30 %endif
30 %endif
31 </ul>
31 </ul>
32 </div>
32 </div>
33
33
34 <div id="repos_list_wrap">
34 <div id="repos_list_wrap">
35 <table id="user_group_list_table" class="display"></table>
35 <table id="user_group_list_table" class="display"></table>
36 </div>
36 </div>
37
37
38 </div>
38 </div>
39 <script>
39 <script>
40 $(document).ready(function() {
40 $(document).ready(function() {
41
41
42 var get_datatable_count = function(){
42 var get_datatable_count = function(){
43 var api = $('#user_group_list_table').dataTable().api();
43 var api = $('#user_group_list_table').dataTable().api();
44 $('#user_group_count').text(api.page.info().recordsDisplay);
44 $('#user_group_count').text(api.page.info().recordsDisplay);
45 };
45 };
46
46
47 // user list
47 // user list
48 $('#user_group_list_table').DataTable({
48 $('#user_group_list_table').DataTable({
49 data: ${c.data|n},
49 data: ${c.data|n},
50 dom: 'rtp',
50 dom: 'rtp',
51 pageLength: ${c.visual.admin_grid_items},
51 pageLength: ${c.visual.admin_grid_items},
52 order: [[ 0, "asc" ]],
52 order: [[ 0, "asc" ]],
53 columns: [
53 columns: [
54 { data: {"_": "group_name",
54 { data: {"_": "group_name",
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
55 "sort": "group_name_raw"}, title: "${_('Name')}", className: "td-componentname" },
56 { data: {"_": "desc",
56 { data: {"_": "desc",
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
57 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
58 { data: {"_": "members",
58 { data: {"_": "members",
59 "sort": "members",
59 "sort": "members",
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
60 "type": Number}, title: "${_('Members')}", className: "td-number" },
61 { data: {"_": "sync",
62 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
61 { data: {"_": "active",
63 { data: {"_": "active",
62 "sort": "active"}, title: "${_('Active')}", className: "td-active", className: "td-number"},
64 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
63 { data: {"_": "owner",
65 { data: {"_": "owner",
64 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
66 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
65 { data: {"_": "action",
67 { data: {"_": "action",
66 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
68 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
67 ],
69 ],
68 language: {
70 language: {
69 paginate: DEFAULT_GRID_PAGINATION,
71 paginate: DEFAULT_GRID_PAGINATION,
70 emptyTable: _gettext("No user groups available yet.")
72 emptyTable: _gettext("No user groups available yet.")
71 },
73 },
72 "initComplete": function( settings, json ) {
74 "initComplete": function( settings, json ) {
73 get_datatable_count();
75 get_datatable_count();
74 }
76 }
75 });
77 });
76
78
77 // update the counter when doing search
79 // update the counter when doing search
78 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
80 $('#user_group_list_table').on( 'search.dt', function (e,settings) {
79 get_datatable_count();
81 get_datatable_count();
80 });
82 });
81
83
82 // filter, filter both grids
84 // filter, filter both grids
83 $('#q_filter').on( 'keyup', function () {
85 $('#q_filter').on( 'keyup', function () {
84 var user_api = $('#user_group_list_table').dataTable().api();
86 var user_api = $('#user_group_list_table').dataTable().api();
85 user_api
87 user_api
86 .columns(0)
88 .columns(0)
87 .search(this.value)
89 .search(this.value)
88 .draw();
90 .draw();
89 });
91 });
90
92
91 // refilter table if page load via back button
93 // refilter table if page load via back button
92 $("#q_filter").trigger('keyup');
94 $("#q_filter").trigger('keyup');
93
95
94 });
96 });
95
97
96 </script>
98 </script>
97
99
98 </%def>
100 </%def>
@@ -1,233 +1,268 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.tests import (
23 from rhodecode.tests import (
24 TestController, url, assert_session_flash, link_to)
24 TestController, url, assert_session_flash, link_to, TEST_USER_ADMIN_LOGIN)
25 from rhodecode.model.db import User, UserGroup
25 from rhodecode.model.db import User, UserGroup
26 from rhodecode.model.meta import Session
26 from rhodecode.model.meta import Session
27 from rhodecode.tests.fixture import Fixture
27 from rhodecode.tests.fixture import Fixture
28
28
29 TEST_USER_GROUP = 'admins_test'
29 TEST_USER_GROUP = 'admins_test'
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33
33
34 class TestAdminUsersGroupsController(TestController):
34 class TestAdminUsersGroupsController(TestController):
35
35
36 def test_index(self):
36 def test_index(self):
37 self.log_user()
37 self.log_user()
38 response = self.app.get(url('users_groups'))
38 response = self.app.get(url('users_groups'))
39 assert response.status_int == 200
39 assert response.status_int == 200
40
40
41 def test_create(self):
41 def test_create(self):
42 self.log_user()
42 self.log_user()
43 users_group_name = TEST_USER_GROUP
43 users_group_name = TEST_USER_GROUP
44 response = self.app.post(url('users_groups'), {
44 response = self.app.post(url('users_groups'), {
45 'users_group_name': users_group_name,
45 'users_group_name': users_group_name,
46 'user_group_description': 'DESC',
46 'user_group_description': 'DESC',
47 'active': True,
47 'active': True,
48 'csrf_token': self.csrf_token})
48 'csrf_token': self.csrf_token})
49
49
50 user_group_link = link_to(
50 user_group_link = link_to(
51 users_group_name,
51 users_group_name,
52 url('edit_users_group',
52 url('edit_users_group',
53 user_group_id=UserGroup.get_by_group_name(
53 user_group_id=UserGroup.get_by_group_name(
54 users_group_name).users_group_id))
54 users_group_name).users_group_id))
55 assert_session_flash(
55 assert_session_flash(
56 response,
56 response,
57 'Created user group %s' % user_group_link)
57 'Created user group %s' % user_group_link)
58
58
59 def test_set_synchronization(self):
60 self.log_user()
61 users_group_name = TEST_USER_GROUP + 'sync'
62 response = self.app.post(url('users_groups'), {
63 'users_group_name': users_group_name,
64 'user_group_description': 'DESC',
65 'active': True,
66 'csrf_token': self.csrf_token})
67
68 group = Session().query(UserGroup).filter(
69 UserGroup.users_group_name == users_group_name).one()
70
71 assert group.group_data.get('extern_type') is None
72
73 # enable
74 self.app.post(
75 url('edit_user_group_advanced_sync', user_group_id=group.users_group_id),
76 params={'csrf_token': self.csrf_token}, status=302)
77
78 group = Session().query(UserGroup).filter(
79 UserGroup.users_group_name == users_group_name).one()
80 assert group.group_data.get('extern_type') == 'manual'
81 assert group.group_data.get('extern_type_set_by') == TEST_USER_ADMIN_LOGIN
82
83 # disable
84 self.app.post(
85 url('edit_user_group_advanced_sync',
86 user_group_id=group.users_group_id),
87 params={'csrf_token': self.csrf_token}, status=302)
88
89 group = Session().query(UserGroup).filter(
90 UserGroup.users_group_name == users_group_name).one()
91 assert group.group_data.get('extern_type') is None
92 assert group.group_data.get('extern_type_set_by') == TEST_USER_ADMIN_LOGIN
93
59 def test_delete(self):
94 def test_delete(self):
60 self.log_user()
95 self.log_user()
61 users_group_name = TEST_USER_GROUP + 'another'
96 users_group_name = TEST_USER_GROUP + 'another'
62 response = self.app.post(url('users_groups'), {
97 response = self.app.post(url('users_groups'), {
63 'users_group_name': users_group_name,
98 'users_group_name': users_group_name,
64 'user_group_description': 'DESC',
99 'user_group_description': 'DESC',
65 'active': True,
100 'active': True,
66 'csrf_token': self.csrf_token})
101 'csrf_token': self.csrf_token})
67
102
68 user_group_link = link_to(
103 user_group_link = link_to(
69 users_group_name,
104 users_group_name,
70 url('edit_users_group',
105 url('edit_users_group',
71 user_group_id=UserGroup.get_by_group_name(
106 user_group_id=UserGroup.get_by_group_name(
72 users_group_name).users_group_id))
107 users_group_name).users_group_id))
73 assert_session_flash(
108 assert_session_flash(
74 response,
109 response,
75 'Created user group %s' % user_group_link)
110 'Created user group %s' % user_group_link)
76
111
77 group = Session().query(UserGroup).filter(
112 group = Session().query(UserGroup).filter(
78 UserGroup.users_group_name == users_group_name).one()
113 UserGroup.users_group_name == users_group_name).one()
79
114
80 response = self.app.post(
115 self.app.post(
81 url('delete_users_group', user_group_id=group.users_group_id),
116 url('delete_users_group', user_group_id=group.users_group_id),
82 params={'_method': 'delete', 'csrf_token': self.csrf_token})
117 params={'_method': 'delete', 'csrf_token': self.csrf_token})
83
118
84 group = Session().query(UserGroup).filter(
119 group = Session().query(UserGroup).filter(
85 UserGroup.users_group_name == users_group_name).scalar()
120 UserGroup.users_group_name == users_group_name).scalar()
86
121
87 assert group is None
122 assert group is None
88
123
89 @pytest.mark.parametrize('repo_create, repo_create_write, user_group_create, repo_group_create, fork_create, inherit_default_permissions, expect_error, expect_form_error', [
124 @pytest.mark.parametrize('repo_create, repo_create_write, user_group_create, repo_group_create, fork_create, inherit_default_permissions, expect_error, expect_form_error', [
90 ('hg.create.none', 'hg.create.write_on_repogroup.false', 'hg.usergroup.create.false', 'hg.repogroup.create.false', 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
125 ('hg.create.none', 'hg.create.write_on_repogroup.false', 'hg.usergroup.create.false', 'hg.repogroup.create.false', 'hg.fork.none', 'hg.inherit_default_perms.false', False, False),
91 ('hg.create.repository', 'hg.create.write_on_repogroup.true', 'hg.usergroup.create.true', 'hg.repogroup.create.true', 'hg.fork.repository', 'hg.inherit_default_perms.false', False, False),
126 ('hg.create.repository', 'hg.create.write_on_repogroup.true', 'hg.usergroup.create.true', 'hg.repogroup.create.true', 'hg.fork.repository', 'hg.inherit_default_perms.false', False, False),
92 ('hg.create.XXX', 'hg.create.write_on_repogroup.true', 'hg.usergroup.create.true', 'hg.repogroup.create.true', 'hg.fork.repository', 'hg.inherit_default_perms.false', False, True),
127 ('hg.create.XXX', 'hg.create.write_on_repogroup.true', 'hg.usergroup.create.true', 'hg.repogroup.create.true', 'hg.fork.repository', 'hg.inherit_default_perms.false', False, True),
93 ('', '', '', '', '', '', True, False),
128 ('', '', '', '', '', '', True, False),
94 ])
129 ])
95 def test_global_perms_on_group(
130 def test_global_perms_on_group(
96 self, repo_create, repo_create_write, user_group_create,
131 self, repo_create, repo_create_write, user_group_create,
97 repo_group_create, fork_create, expect_error, expect_form_error,
132 repo_group_create, fork_create, expect_error, expect_form_error,
98 inherit_default_permissions):
133 inherit_default_permissions):
99 self.log_user()
134 self.log_user()
100 users_group_name = TEST_USER_GROUP + 'another2'
135 users_group_name = TEST_USER_GROUP + 'another2'
101 response = self.app.post(url('users_groups'),
136 response = self.app.post(url('users_groups'),
102 {'users_group_name': users_group_name,
137 {'users_group_name': users_group_name,
103 'user_group_description': 'DESC',
138 'user_group_description': 'DESC',
104 'active': True,
139 'active': True,
105 'csrf_token': self.csrf_token})
140 'csrf_token': self.csrf_token})
106
141
107 ug = UserGroup.get_by_group_name(users_group_name)
142 ug = UserGroup.get_by_group_name(users_group_name)
108 user_group_link = link_to(
143 user_group_link = link_to(
109 users_group_name,
144 users_group_name,
110 url('edit_users_group', user_group_id=ug.users_group_id))
145 url('edit_users_group', user_group_id=ug.users_group_id))
111 assert_session_flash(
146 assert_session_flash(
112 response,
147 response,
113 'Created user group %s' % user_group_link)
148 'Created user group %s' % user_group_link)
114 response.follow()
149 response.follow()
115
150
116 # ENABLE REPO CREATE ON A GROUP
151 # ENABLE REPO CREATE ON A GROUP
117 perm_params = {
152 perm_params = {
118 'inherit_default_permissions': False,
153 'inherit_default_permissions': False,
119 'default_repo_create': repo_create,
154 'default_repo_create': repo_create,
120 'default_repo_create_on_write': repo_create_write,
155 'default_repo_create_on_write': repo_create_write,
121 'default_user_group_create': user_group_create,
156 'default_user_group_create': user_group_create,
122 'default_repo_group_create': repo_group_create,
157 'default_repo_group_create': repo_group_create,
123 'default_fork_create': fork_create,
158 'default_fork_create': fork_create,
124 'default_inherit_default_permissions': inherit_default_permissions,
159 'default_inherit_default_permissions': inherit_default_permissions,
125
160
126 '_method': 'put',
161 '_method': 'put',
127 'csrf_token': self.csrf_token,
162 'csrf_token': self.csrf_token,
128 }
163 }
129 response = self.app.post(
164 response = self.app.post(
130 url('edit_user_group_global_perms',
165 url('edit_user_group_global_perms',
131 user_group_id=ug.users_group_id),
166 user_group_id=ug.users_group_id),
132 params=perm_params)
167 params=perm_params)
133
168
134 if expect_form_error:
169 if expect_form_error:
135 assert response.status_int == 200
170 assert response.status_int == 200
136 response.mustcontain('Value must be one of')
171 response.mustcontain('Value must be one of')
137 else:
172 else:
138 if expect_error:
173 if expect_error:
139 msg = 'An error occurred during permissions saving'
174 msg = 'An error occurred during permissions saving'
140 else:
175 else:
141 msg = 'User Group global permissions updated successfully'
176 msg = 'User Group global permissions updated successfully'
142 ug = UserGroup.get_by_group_name(users_group_name)
177 ug = UserGroup.get_by_group_name(users_group_name)
143 del perm_params['_method']
178 del perm_params['_method']
144 del perm_params['csrf_token']
179 del perm_params['csrf_token']
145 del perm_params['inherit_default_permissions']
180 del perm_params['inherit_default_permissions']
146 assert perm_params == ug.get_default_perms()
181 assert perm_params == ug.get_default_perms()
147 assert_session_flash(response, msg)
182 assert_session_flash(response, msg)
148
183
149 fixture.destroy_user_group(users_group_name)
184 fixture.destroy_user_group(users_group_name)
150
185
151 def test_edit_autocomplete(self):
186 def test_edit_autocomplete(self):
152 self.log_user()
187 self.log_user()
153 ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True)
188 ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True)
154 response = self.app.get(
189 response = self.app.get(
155 url('edit_users_group', user_group_id=ug.users_group_id))
190 url('edit_users_group', user_group_id=ug.users_group_id))
156 fixture.destroy_user_group(TEST_USER_GROUP)
191 fixture.destroy_user_group(TEST_USER_GROUP)
157
192
158 def test_edit_user_group_autocomplete_members(self, xhr_header):
193 def test_edit_user_group_autocomplete_members(self, xhr_header):
159 self.log_user()
194 self.log_user()
160 ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True)
195 ug = fixture.create_user_group(TEST_USER_GROUP, skip_if_exists=True)
161 response = self.app.get(
196 response = self.app.get(
162 url('edit_user_group_members', user_group_id=ug.users_group_id),
197 url('edit_user_group_members', user_group_id=ug.users_group_id),
163 extra_environ=xhr_header)
198 extra_environ=xhr_header)
164
199
165 assert response.body == '{"members": []}'
200 assert response.body == '{"members": []}'
166 fixture.destroy_user_group(TEST_USER_GROUP)
201 fixture.destroy_user_group(TEST_USER_GROUP)
167
202
168 def test_usergroup_escape(self):
203 def test_usergroup_escape(self):
169 user = User.get_by_username('test_admin')
204 user = User.get_by_username('test_admin')
170 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
205 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
171 user.lastname = (
206 user.lastname = (
172 '<img src="/image2" onload="alert(\'Hello, World!\');">')
207 '<img src="/image2" onload="alert(\'Hello, World!\');">')
173 Session().add(user)
208 Session().add(user)
174 Session().commit()
209 Session().commit()
175
210
176 self.log_user()
211 self.log_user()
177 users_group_name = 'samplegroup'
212 users_group_name = 'samplegroup'
178 data = {
213 data = {
179 'users_group_name': users_group_name,
214 'users_group_name': users_group_name,
180 'user_group_description': (
215 'user_group_description': (
181 '<strong onload="alert();">DESC</strong>'),
216 '<strong onload="alert();">DESC</strong>'),
182 'active': True,
217 'active': True,
183 'csrf_token': self.csrf_token
218 'csrf_token': self.csrf_token
184 }
219 }
185
220
186 self.app.post(url('users_groups'), data)
221 self.app.post(url('users_groups'), data)
187 response = self.app.get(url('users_groups'))
222 response = self.app.get(url('users_groups'))
188
223
189 response.mustcontain(
224 response.mustcontain(
190 '&lt;strong onload=&#34;alert();&#34;&gt;'
225 '&lt;strong onload=&#34;alert();&#34;&gt;'
191 'DESC&lt;/strong&gt;')
226 'DESC&lt;/strong&gt;')
192 response.mustcontain(
227 response.mustcontain(
193 '&lt;img src=&#34;/image2&#34; onload=&#34;'
228 '&lt;img src=&#34;/image2&#34; onload=&#34;'
194 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
229 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
195
230
196 def test_update_members_from_user_ids(self, user_regular):
231 def test_update_members_from_user_ids(self, user_regular):
197 uid = user_regular.user_id
232 uid = user_regular.user_id
198 username = user_regular.username
233 username = user_regular.username
199 self.log_user()
234 self.log_user()
200
235
201 user_group = fixture.create_user_group('test_gr_ids')
236 user_group = fixture.create_user_group('test_gr_ids')
202 assert user_group.members == []
237 assert user_group.members == []
203 assert user_group.user != user_regular
238 assert user_group.user != user_regular
204 expected_active_state = not user_group.users_group_active
239 expected_active_state = not user_group.users_group_active
205
240
206 form_data = [
241 form_data = [
207 ('csrf_token', self.csrf_token),
242 ('csrf_token', self.csrf_token),
208 ('_method', 'put'),
243 ('_method', 'put'),
209 ('user', username),
244 ('user', username),
210 ('users_group_name', 'changed_name'),
245 ('users_group_name', 'changed_name'),
211 ('users_group_active', expected_active_state),
246 ('users_group_active', expected_active_state),
212 ('user_group_description', 'changed_description'),
247 ('user_group_description', 'changed_description'),
213
248
214 ('__start__', 'user_group_members:sequence'),
249 ('__start__', 'user_group_members:sequence'),
215 ('__start__', 'member:mapping'),
250 ('__start__', 'member:mapping'),
216 ('member_user_id', uid),
251 ('member_user_id', uid),
217 ('type', 'existing'),
252 ('type', 'existing'),
218 ('__end__', 'member:mapping'),
253 ('__end__', 'member:mapping'),
219 ('__end__', 'user_group_members:sequence'),
254 ('__end__', 'user_group_members:sequence'),
220 ]
255 ]
221 ugid = user_group.users_group_id
256 ugid = user_group.users_group_id
222 self.app.post(url('update_users_group', user_group_id=ugid), form_data)
257 self.app.post(url('update_users_group', user_group_id=ugid), form_data)
223
258
224 user_group = UserGroup.get(ugid)
259 user_group = UserGroup.get(ugid)
225 assert user_group
260 assert user_group
226
261
227 assert user_group.members[0].user_id == uid
262 assert user_group.members[0].user_id == uid
228 assert user_group.user_id == uid
263 assert user_group.user_id == uid
229 assert 'changed_name' in user_group.users_group_name
264 assert 'changed_name' in user_group.users_group_name
230 assert 'changed_description' in user_group.user_group_description
265 assert 'changed_description' in user_group.user_group_description
231 assert user_group.users_group_active == expected_active_state
266 assert user_group.users_group_active == expected_active_state
232
267
233 fixture.destroy_user_group(user_group)
268 fixture.destroy_user_group(user_group)
General Comments 0
You need to be logged in to leave comments. Login now