##// END OF EJS Templates
audit-logs: added action logs for repository groups.
marcink -
r1799:e51d88d8 default
parent child Browse files
Show More
@@ -1,404 +1,406 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 Repository groups controller for RhodeCode
24 24 """
25 25
26 26 import logging
27 27 import formencode
28 28
29 29 from formencode import htmlfill
30 30
31 31 from pylons import request, tmpl_context as c, url
32 32 from pylons.controllers.util import abort, redirect
33 33 from pylons.i18n.translation import _, ungettext
34 34
35 35 from rhodecode.lib import auth
36 36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import audit_logger
37 38 from rhodecode.lib.ext_json import json
38 39 from rhodecode.lib.auth import (
39 40 LoginRequired, NotAnonymous, HasPermissionAll,
40 41 HasRepoGroupPermissionAll, HasRepoGroupPermissionAnyDecorator)
41 42 from rhodecode.lib.base import BaseController, render
42 43 from rhodecode.lib.utils2 import safe_int
43 44 from rhodecode.model.db import RepoGroup, User
44 45 from rhodecode.model.scm import RepoGroupList
45 46 from rhodecode.model.repo_group import RepoGroupModel
46 47 from rhodecode.model.forms import RepoGroupForm, RepoGroupPermsForm
47 48 from rhodecode.model.meta import Session
48 49
49 50
50 51 log = logging.getLogger(__name__)
51 52
52 53
53 54 class RepoGroupsController(BaseController):
54 55 """REST Controller styled on the Atom Publishing Protocol"""
55 56
56 57 @LoginRequired()
57 58 def __before__(self):
58 59 super(RepoGroupsController, self).__before__()
59 60
60 61 def __load_defaults(self, allow_empty_group=False, repo_group=None):
61 62 if self._can_create_repo_group():
62 63 # we're global admin, we're ok and we can create TOP level groups
63 64 allow_empty_group = True
64 65
65 66 # override the choices for this form, we need to filter choices
66 67 # and display only those we have ADMIN right
67 68 groups_with_admin_rights = RepoGroupList(
68 69 RepoGroup.query().all(),
69 70 perm_set=['group.admin'])
70 71 c.repo_groups = RepoGroup.groups_choices(
71 72 groups=groups_with_admin_rights,
72 73 show_empty_group=allow_empty_group)
73 74
74 75 if repo_group:
75 76 # exclude filtered ids
76 77 exclude_group_ids = [repo_group.group_id]
77 78 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
78 79 c.repo_groups)
79 80 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
80 81 parent_group = repo_group.parent_group
81 82
82 83 add_parent_group = (parent_group and (
83 84 unicode(parent_group.group_id) not in c.repo_groups_choices))
84 85 if add_parent_group:
85 86 c.repo_groups_choices.append(unicode(parent_group.group_id))
86 87 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
87 88
88 89 def __load_data(self, group_id):
89 90 """
90 91 Load defaults settings for edit, and update
91 92
92 93 :param group_id:
93 94 """
94 95 repo_group = RepoGroup.get_or_404(group_id)
95 96 data = repo_group.get_dict()
96 97 data['group_name'] = repo_group.name
97 98
98 99 # fill owner
99 100 if repo_group.user:
100 101 data.update({'user': repo_group.user.username})
101 102 else:
102 103 replacement_user = User.get_first_super_admin().username
103 104 data.update({'user': replacement_user})
104 105
105 106 # fill repository group users
106 107 for p in repo_group.repo_group_to_perm:
107 108 data.update({
108 109 'u_perm_%s' % p.user.user_id: p.permission.permission_name})
109 110
110 111 # fill repository group user groups
111 112 for p in repo_group.users_group_to_perm:
112 113 data.update({
113 114 'g_perm_%s' % p.users_group.users_group_id:
114 115 p.permission.permission_name})
115 116 # html and form expects -1 as empty parent group
116 117 data['group_parent_id'] = data['group_parent_id'] or -1
117 118 return data
118 119
119 120 def _revoke_perms_on_yourself(self, form_result):
120 121 _updates = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
121 122 form_result['perm_updates'])
122 123 _additions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
123 124 form_result['perm_additions'])
124 125 _deletions = filter(lambda u: c.rhodecode_user.user_id == int(u[0]),
125 126 form_result['perm_deletions'])
126 127 admin_perm = 'group.admin'
127 128 if _updates and _updates[0][1] != admin_perm or \
128 129 _additions and _additions[0][1] != admin_perm or \
129 130 _deletions and _deletions[0][1] != admin_perm:
130 131 return True
131 132 return False
132 133
133 134 def _can_create_repo_group(self, parent_group_id=None):
134 135 is_admin = HasPermissionAll('hg.admin')('group create controller')
135 136 create_repo_group = HasPermissionAll(
136 137 'hg.repogroup.create.true')('group create controller')
137 138 if is_admin or (create_repo_group and not parent_group_id):
138 139 # we're global admin, or we have global repo group create
139 140 # permission
140 141 # we're ok and we can create TOP level groups
141 142 return True
142 143 elif parent_group_id:
143 144 # we check the permission if we can write to parent group
144 145 group = RepoGroup.get(parent_group_id)
145 146 group_name = group.group_name if group else None
146 147 if HasRepoGroupPermissionAll('group.admin')(
147 148 group_name, 'check if user is an admin of group'):
148 149 # we're an admin of passed in group, we're ok.
149 150 return True
150 151 else:
151 152 return False
152 153 return False
153 154
154 155 @NotAnonymous()
155 156 def index(self):
156 """GET /repo_groups: All items in the collection"""
157 # url('repo_groups')
158
159 157 repo_group_list = RepoGroup.get_all_repo_groups()
160 158 _perms = ['group.admin']
161 159 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
162 160 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
163 161 repo_group_list=repo_group_list_acl, admin=True)
164 162 c.data = json.dumps(repo_group_data)
165 163 return render('admin/repo_groups/repo_groups.mako')
166 164
167 165 # perm checks inside
168 166 @NotAnonymous()
169 167 @auth.CSRFRequired()
170 168 def create(self):
171 """POST /repo_groups: Create a new item"""
172 # url('repo_groups')
173 169
174 170 parent_group_id = safe_int(request.POST.get('group_parent_id'))
175 171 can_create = self._can_create_repo_group(parent_group_id)
176 172
177 173 self.__load_defaults()
178 174 # permissions for can create group based on parent_id are checked
179 175 # here in the Form
180 176 available_groups = map(lambda k: unicode(k[0]), c.repo_groups)
181 177 repo_group_form = RepoGroupForm(available_groups=available_groups,
182 178 can_create_in_root=can_create)()
183 179 try:
184 180 owner = c.rhodecode_user
185 181 form_result = repo_group_form.to_python(dict(request.POST))
186 RepoGroupModel().create(
182 repo_group = RepoGroupModel().create(
187 183 group_name=form_result['group_name_full'],
188 184 group_description=form_result['group_description'],
189 185 owner=owner.user_id,
190 186 copy_permissions=form_result['group_copy_permissions']
191 187 )
192 188 Session().commit()
189 repo_group_data = repo_group.get_api_data()
193 190 _new_group_name = form_result['group_name_full']
191
192 audit_logger.store(
193 action='repo_group.create',
194 action_data={'repo_group_data': repo_group_data},
195 user=c.rhodecode_user, commit=True)
196
194 197 repo_group_url = h.link_to(
195 198 _new_group_name,
196 199 h.route_path('repo_group_home', repo_group_name=_new_group_name))
197 200 h.flash(h.literal(_('Created repository group %s')
198 201 % repo_group_url), category='success')
199 # TODO: in future action_logger(, '', '', '', self.sa)
202
200 203 except formencode.Invalid as errors:
201 204 return htmlfill.render(
202 205 render('admin/repo_groups/repo_group_add.mako'),
203 206 defaults=errors.value,
204 207 errors=errors.error_dict or {},
205 208 prefix_error=False,
206 209 encoding="UTF-8",
207 210 force_defaults=False)
208 211 except Exception:
209 212 log.exception("Exception during creation of repository group")
210 213 h.flash(_('Error occurred during creation of repository group %s')
211 214 % request.POST.get('group_name'), category='error')
212 215
213 216 # TODO: maybe we should get back to the main view, not the admin one
214 217 return redirect(url('repo_groups', parent_group=parent_group_id))
215 218
216 219 # perm checks inside
217 220 @NotAnonymous()
218 221 def new(self):
219 """GET /repo_groups/new: Form to create a new item"""
220 # url('new_repo_group')
221 222 # perm check for admin, create_group perm or admin of parent_group
222 223 parent_group_id = safe_int(request.GET.get('parent_group'))
223 224 if not self._can_create_repo_group(parent_group_id):
224 225 return abort(403)
225 226
226 227 self.__load_defaults()
227 228 return render('admin/repo_groups/repo_group_add.mako')
228 229
229 230 @HasRepoGroupPermissionAnyDecorator('group.admin')
230 231 @auth.CSRFRequired()
231 232 def update(self, group_name):
232 """PUT /repo_groups/group_name: Update an existing item"""
233 # Forms posted to this method should contain a hidden field:
234 # <input type="hidden" name="_method" value="PUT" />
235 # Or using helpers:
236 # h.form(url('repos_group', group_name=GROUP_NAME), method='put')
237 233
238 234 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
239 235 can_create_in_root = self._can_create_repo_group()
240 236 show_root_location = can_create_in_root
241 237 if not c.repo_group.parent_group:
242 238 # this group don't have a parrent so we should show empty value
243 239 show_root_location = True
244 240 self.__load_defaults(allow_empty_group=show_root_location,
245 241 repo_group=c.repo_group)
246 242
247 243 repo_group_form = RepoGroupForm(
248 244 edit=True, old_data=c.repo_group.get_dict(),
249 245 available_groups=c.repo_groups_choices,
250 246 can_create_in_root=can_create_in_root, allow_disabled=True)()
251 247
248 old_values = c.repo_group.get_api_data()
252 249 try:
253 250 form_result = repo_group_form.to_python(dict(request.POST))
254 251 gr_name = form_result['group_name']
255 252 new_gr = RepoGroupModel().update(group_name, form_result)
253
254 audit_logger.store(
255 'repo_group.edit', action_data={'old_data': old_values},
256 user=c.rhodecode_user)
257
256 258 Session().commit()
257 259 h.flash(_('Updated repository group %s') % (gr_name,),
258 260 category='success')
259 261 # we now have new name !
260 262 group_name = new_gr.group_name
261 # TODO: in future action_logger(, '', '', '', self.sa)
262 263 except formencode.Invalid as errors:
263 264 c.active = 'settings'
264 265 return htmlfill.render(
265 266 render('admin/repo_groups/repo_group_edit.mako'),
266 267 defaults=errors.value,
267 268 errors=errors.error_dict or {},
268 269 prefix_error=False,
269 270 encoding="UTF-8",
270 271 force_defaults=False)
271 272 except Exception:
272 273 log.exception("Exception during update or repository group")
273 274 h.flash(_('Error occurred during update of repository group %s')
274 275 % request.POST.get('group_name'), category='error')
275 276
276 277 return redirect(url('edit_repo_group', group_name=group_name))
277 278
278 279 @HasRepoGroupPermissionAnyDecorator('group.admin')
279 280 @auth.CSRFRequired()
280 281 def delete(self, group_name):
281 """DELETE /repo_groups/group_name: Delete an existing item"""
282 # Forms posted to this method should contain a hidden field:
283 # <input type="hidden" name="_method" value="DELETE" />
284 # Or using helpers:
285 # h.form(url('repos_group', group_name=GROUP_NAME), method='delete')
286
287 282 gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name)
288 283 repos = gr.repositories.all()
289 284 if repos:
290 285 msg = ungettext(
291 286 'This group contains %(num)d repository and cannot be deleted',
292 287 'This group contains %(num)d repositories and cannot be'
293 288 ' deleted',
294 289 len(repos)) % {'num': len(repos)}
295 290 h.flash(msg, category='warning')
296 291 return redirect(url('repo_groups'))
297 292
298 293 children = gr.children.all()
299 294 if children:
300 295 msg = ungettext(
301 296 'This group contains %(num)d subgroup and cannot be deleted',
302 297 'This group contains %(num)d subgroups and cannot be deleted',
303 298 len(children)) % {'num': len(children)}
304 299 h.flash(msg, category='warning')
305 300 return redirect(url('repo_groups'))
306 301
307 302 try:
303 old_values = gr.get_api_data()
308 304 RepoGroupModel().delete(group_name)
305
306 audit_logger.store(
307 'repo_group.delete',
308 action_data={'old_data': old_values,
309 'source': audit_logger.SOURCE_WEB},
310 user=c.rhodecode_user)
311
309 312 Session().commit()
310 313 h.flash(_('Removed repository group %s') % group_name,
311 314 category='success')
312 # TODO: in future action_logger(, '', '', '', self.sa)
313 315 except Exception:
314 316 log.exception("Exception during deletion of repository group")
315 317 h.flash(_('Error occurred during deletion of repository group %s')
316 318 % group_name, category='error')
317 319
318 320 return redirect(url('repo_groups'))
319 321
320 322 @HasRepoGroupPermissionAnyDecorator('group.admin')
321 323 def edit(self, group_name):
322 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
323 # url('edit_repo_group', group_name=GROUP_NAME)
324
324 325 c.active = 'settings'
325 326
326 327 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
327 328 # we can only allow moving empty group if it's already a top-level
328 329 # group, ie has no parents, or we're admin
329 330 can_create_in_root = self._can_create_repo_group()
330 331 show_root_location = can_create_in_root
331 332 if not c.repo_group.parent_group:
332 333 # this group don't have a parrent so we should show empty value
333 334 show_root_location = True
334 335 self.__load_defaults(allow_empty_group=show_root_location,
335 336 repo_group=c.repo_group)
336 337 defaults = self.__load_data(c.repo_group.group_id)
337 338
338 339 return htmlfill.render(
339 340 render('admin/repo_groups/repo_group_edit.mako'),
340 341 defaults=defaults,
341 342 encoding="UTF-8",
342 343 force_defaults=False
343 344 )
344 345
345 346 @HasRepoGroupPermissionAnyDecorator('group.admin')
346 347 def edit_repo_group_advanced(self, group_name):
347 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
348 # url('edit_repo_group', group_name=GROUP_NAME)
349 348 c.active = 'advanced'
350 349 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
351 350
352 351 return render('admin/repo_groups/repo_group_edit.mako')
353 352
354 353 @HasRepoGroupPermissionAnyDecorator('group.admin')
355 354 def edit_repo_group_perms(self, group_name):
356 """GET /repo_groups/group_name/edit: Form to edit an existing item"""
357 # url('edit_repo_group', group_name=GROUP_NAME)
358 355 c.active = 'perms'
359 356 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
360 357 self.__load_defaults()
361 358 defaults = self.__load_data(c.repo_group.group_id)
362 359
363 360 return htmlfill.render(
364 361 render('admin/repo_groups/repo_group_edit.mako'),
365 362 defaults=defaults,
366 363 encoding="UTF-8",
367 364 force_defaults=False
368 365 )
369 366
370 367 @HasRepoGroupPermissionAnyDecorator('group.admin')
371 368 @auth.CSRFRequired()
372 369 def update_perms(self, group_name):
373 370 """
374 371 Update permissions for given repository group
375
376 :param group_name:
377 372 """
378 373
379 374 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
380 375 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
381 376 form = RepoGroupPermsForm(valid_recursive_choices)().to_python(
382 377 request.POST)
383 378
384 379 if not c.rhodecode_user.is_admin:
385 380 if self._revoke_perms_on_yourself(form):
386 381 msg = _('Cannot change permission for yourself as admin')
387 382 h.flash(msg, category='warning')
388 383 return redirect(
389 384 url('edit_repo_group_perms', group_name=group_name))
390 385
391 386 # iterate over all members(if in recursive mode) of this groups and
392 387 # set the permissions !
393 388 # this can be potentially heavy operation
394 RepoGroupModel().update_permissions(
389 changes = RepoGroupModel().update_permissions(
395 390 c.repo_group,
396 form['perm_additions'], form['perm_updates'],
397 form['perm_deletions'], form['recursive'])
391 form['perm_additions'], form['perm_updates'], form['perm_deletions'],
392 form['recursive'])
398 393
399 # TODO: implement this
400 # action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
401 # repo_name, self.ip_addr, self.sa)
394 action_data = {
395 'added': changes['added'],
396 'updated': changes['updated'],
397 'deleted': changes['deleted'],
398 'source': audit_logger.SOURCE_WEB
399 }
400 audit_logger.store(
401 'repo_group.edit.permissions', action_data=action_data,
402 user=c.rhodecode_user)
403
402 404 Session().commit()
403 405 h.flash(_('Repository Group permissions updated'), category='success')
404 406 return redirect(url('edit_repo_group_perms', group_name=group_name))
@@ -1,188 +1,193 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import datetime
23 23
24 24 from rhodecode.model import meta
25 25 from rhodecode.model.db import User, UserLog, Repository
26 26
27 27
28 28 log = logging.getLogger(__name__)
29 29
30 30
31 31 ACTIONS = {
32 32 'user.login.success': {},
33 33 'user.login.failure': {},
34 34 'user.logout': {},
35 35 'user.password.reset_request': {},
36 36 'user.push': {},
37 37 'user.pull': {},
38 38
39 39 'repo.create': {},
40 40 'repo.edit': {},
41 41 'repo.edit.permissions': {},
42 42 'repo.delete': {},
43 43 'repo.commit.strip': {},
44 44 'repo.archive.download': {},
45
46 'repo_group.create': {},
47 'repo_group.edit': {},
48 'repo_group.edit.permissions': {},
49 'repo_group.delete': {},
45 50 }
46 51
47 52 SOURCE_WEB = 'source_web'
48 53 SOURCE_API = 'source_api'
49 54
50 55
51 56 class UserWrap(object):
52 57 """
53 58 Fake object used to imitate AuthUser
54 59 """
55 60
56 61 def __init__(self, user_id=None, username=None, ip_addr=None):
57 62 self.user_id = user_id
58 63 self.username = username
59 64 self.ip_addr = ip_addr
60 65
61 66
62 67 class RepoWrap(object):
63 68 """
64 69 Fake object used to imitate RepoObject that audit logger requires
65 70 """
66 71
67 72 def __init__(self, repo_id=None, repo_name=None):
68 73 self.repo_id = repo_id
69 74 self.repo_name = repo_name
70 75
71 76
72 77 def _store_log(action_name, action_data, user_id, username, user_data,
73 78 ip_address, repository_id, repository_name):
74 79 user_log = UserLog()
75 80 user_log.version = UserLog.VERSION_2
76 81
77 82 user_log.action = action_name
78 83 user_log.action_data = action_data
79 84
80 85 user_log.user_ip = ip_address
81 86
82 87 user_log.user_id = user_id
83 88 user_log.username = username
84 89 user_log.user_data = user_data
85 90
86 91 user_log.repository_id = repository_id
87 92 user_log.repository_name = repository_name
88 93
89 94 user_log.action_date = datetime.datetime.now()
90 95
91 96 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
92 97 action_name, user_id, username, ip_address)
93 98
94 99 return user_log
95 100
96 101
97 102 def store(
98 103 action, user, action_data=None, user_data=None, ip_addr=None,
99 104 repo=None, sa_session=None, commit=False):
100 105 """
101 106 Audit logger for various actions made by users, typically this results in a call such::
102 107
103 108 from rhodecode.lib import audit_logger
104 109
105 110 audit_logger.store(
106 111 action='repo.edit', user=self._rhodecode_user)
107 112 audit_logger.store(
108 113 action='repo.delete', action_data={'repo_data': repo_data},
109 114 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
110 115
111 116 # repo action
112 117 audit_logger.store(
113 118 action='repo.delete',
114 119 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
115 120 repo=audit_logger.RepoWrap(repo_name='some-repo'))
116 121
117 122 # repo action, when we know and have the repository object already
118 123 audit_logger.store(
119 124 action='repo.delete',
120 125 action_data={'source': audit_logger.SOURCE_WEB, },
121 126 user=self._rhodecode_user,
122 127 repo=repo_object)
123 128
124 129 # without an user ?
125 130 audit_logger.store(
126 131 action='user.login.failure',
127 132 user=audit_logger.UserWrap(
128 133 username=self.request.params.get('username'),
129 134 ip_addr=self.request.remote_addr))
130 135
131 136 """
132 137 from rhodecode.lib.utils2 import safe_unicode
133 138 from rhodecode.lib.auth import AuthUser
134 139
135 140 if action not in ACTIONS:
136 141 raise ValueError('Action `{}` not in valid actions'.format(action))
137 142
138 143 if not sa_session:
139 144 sa_session = meta.Session()
140 145
141 146 try:
142 147 username = getattr(user, 'username', None)
143 148 if not username:
144 149 pass
145 150
146 151 user_id = getattr(user, 'user_id', None)
147 152 if not user_id:
148 153 # maybe we have username ? Try to figure user_id from username
149 154 if username:
150 155 user_id = getattr(
151 156 User.get_by_username(username), 'user_id', None)
152 157
153 158 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
154 159 if not ip_addr:
155 160 pass
156 161
157 162 if not user_data:
158 163 # try to get this from the auth user
159 164 if isinstance(user, AuthUser):
160 165 user_data = {
161 166 'username': user.username,
162 167 'email': user.email,
163 168 }
164 169
165 170 repository_name = getattr(repo, 'repo_name', None)
166 171 repository_id = getattr(repo, 'repo_id', None)
167 172 if not repository_id:
168 173 # maybe we have repo_name ? Try to figure repo_id from repo_name
169 174 if repository_name:
170 175 repository_id = getattr(
171 176 Repository.get_by_repo_name(repository_name), 'repo_id', None)
172 177
173 178 user_log = _store_log(
174 179 action_name=safe_unicode(action),
175 180 action_data=action_data or {},
176 181 user_id=user_id,
177 182 username=username,
178 183 user_data=user_data or {},
179 184 ip_address=safe_unicode(ip_addr),
180 185 repository_id=repository_id,
181 186 repository_name=repository_name
182 187 )
183 188 sa_session.add(user_log)
184 189 if commit:
185 190 sa_session.commit()
186 191
187 192 except Exception:
188 193 log.exception('AUDIT: failed to store audit log')
@@ -1,712 +1,733 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 repo group model for RhodeCode
24 24 """
25 25
26 26 import os
27 27 import datetime
28 28 import itertools
29 29 import logging
30 30 import shutil
31 31 import traceback
32 32 import string
33 33
34 34 from zope.cachedescriptors.property import Lazy as LazyProperty
35 35
36 36 from rhodecode import events
37 37 from rhodecode.model import BaseModel
38 38 from rhodecode.model.db import (_hash_key,
39 39 RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 40 UserGroup, Repository)
41 41 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
42 42 from rhodecode.lib.caching_query import FromCache
43 43 from rhodecode.lib.utils2 import action_logger_generic
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class RepoGroupModel(BaseModel):
49 49
50 50 cls = RepoGroup
51 51 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 52 PERSONAL_GROUP_PATTERN = '${username}' # default
53 53
54 54 def _get_user_group(self, users_group):
55 55 return self._get_instance(UserGroup, users_group,
56 56 callback=UserGroup.get_by_group_name)
57 57
58 58 def _get_repo_group(self, repo_group):
59 59 return self._get_instance(RepoGroup, repo_group,
60 60 callback=RepoGroup.get_by_group_name)
61 61
62 62 @LazyProperty
63 63 def repos_path(self):
64 64 """
65 65 Gets the repositories root path from database
66 66 """
67 67
68 68 settings_model = VcsSettingsModel(sa=self.sa)
69 69 return settings_model.get_repos_location()
70 70
71 71 def get_by_group_name(self, repo_group_name, cache=None):
72 72 repo = self.sa.query(RepoGroup) \
73 73 .filter(RepoGroup.group_name == repo_group_name)
74 74
75 75 if cache:
76 76 name_key = _hash_key(repo_group_name)
77 77 repo = repo.options(
78 78 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
79 79 return repo.scalar()
80 80
81 81 def get_default_create_personal_repo_group(self):
82 82 value = SettingsModel().get_setting_by_name(
83 83 'create_personal_repo_group')
84 84 return value.app_settings_value if value else None or False
85 85
86 86 def get_personal_group_name_pattern(self):
87 87 value = SettingsModel().get_setting_by_name(
88 88 'personal_repo_group_pattern')
89 89 val = value.app_settings_value if value else None
90 90 group_template = val or self.PERSONAL_GROUP_PATTERN
91 91
92 92 group_template = group_template.lstrip('/')
93 93 return group_template
94 94
95 95 def get_personal_group_name(self, user):
96 96 template = self.get_personal_group_name_pattern()
97 97 return string.Template(template).safe_substitute(
98 98 username=user.username,
99 99 user_id=user.user_id,
100 100 )
101 101
102 102 def create_personal_repo_group(self, user, commit_early=True):
103 103 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
104 104 personal_repo_group_name = self.get_personal_group_name(user)
105 105
106 106 # create a new one
107 107 RepoGroupModel().create(
108 108 group_name=personal_repo_group_name,
109 109 group_description=desc,
110 110 owner=user.username,
111 111 personal=True,
112 112 commit_early=commit_early)
113 113
114 114 def _create_default_perms(self, new_group):
115 115 # create default permission
116 116 default_perm = 'group.read'
117 117 def_user = User.get_default_user()
118 118 for p in def_user.user_perms:
119 119 if p.permission.permission_name.startswith('group.'):
120 120 default_perm = p.permission.permission_name
121 121 break
122 122
123 123 repo_group_to_perm = UserRepoGroupToPerm()
124 124 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
125 125
126 126 repo_group_to_perm.group = new_group
127 127 repo_group_to_perm.user_id = def_user.user_id
128 128 return repo_group_to_perm
129 129
130 130 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
131 131 get_object=False):
132 132 """
133 133 Get's the group name and a parent group name from given group name.
134 134 If repo_in_path is set to truth, we asume the full path also includes
135 135 repo name, in such case we clean the last element.
136 136
137 137 :param group_name_full:
138 138 """
139 139 split_paths = 1
140 140 if repo_in_path:
141 141 split_paths = 2
142 142 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
143 143
144 144 if repo_in_path and len(_parts) > 1:
145 145 # such case last element is the repo_name
146 146 _parts.pop(-1)
147 147 group_name_cleaned = _parts[-1] # just the group name
148 148 parent_repo_group_name = None
149 149
150 150 if len(_parts) > 1:
151 151 parent_repo_group_name = _parts[0]
152 152
153 153 parent_group = None
154 154 if parent_repo_group_name:
155 155 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
156 156
157 157 if get_object:
158 158 return group_name_cleaned, parent_repo_group_name, parent_group
159 159
160 160 return group_name_cleaned, parent_repo_group_name
161 161
162 162 def check_exist_filesystem(self, group_name, exc_on_failure=True):
163 163 create_path = os.path.join(self.repos_path, group_name)
164 164 log.debug('creating new group in %s', create_path)
165 165
166 166 if os.path.isdir(create_path):
167 167 if exc_on_failure:
168 168 abs_create_path = os.path.abspath(create_path)
169 169 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
170 170 return False
171 171 return True
172 172
173 173 def _create_group(self, group_name):
174 174 """
175 175 makes repository group on filesystem
176 176
177 177 :param repo_name:
178 178 :param parent_id:
179 179 """
180 180
181 181 self.check_exist_filesystem(group_name)
182 182 create_path = os.path.join(self.repos_path, group_name)
183 183 log.debug('creating new group in %s', create_path)
184 184 os.makedirs(create_path, mode=0755)
185 185 log.debug('created group in %s', create_path)
186 186
187 187 def _rename_group(self, old, new):
188 188 """
189 189 Renames a group on filesystem
190 190
191 191 :param group_name:
192 192 """
193 193
194 194 if old == new:
195 195 log.debug('skipping group rename')
196 196 return
197 197
198 198 log.debug('renaming repository group from %s to %s', old, new)
199 199
200 200 old_path = os.path.join(self.repos_path, old)
201 201 new_path = os.path.join(self.repos_path, new)
202 202
203 203 log.debug('renaming repos paths from %s to %s', old_path, new_path)
204 204
205 205 if os.path.isdir(new_path):
206 206 raise Exception('Was trying to rename to already '
207 207 'existing dir %s' % new_path)
208 208 shutil.move(old_path, new_path)
209 209
210 210 def _delete_filesystem_group(self, group, force_delete=False):
211 211 """
212 212 Deletes a group from a filesystem
213 213
214 214 :param group: instance of group from database
215 215 :param force_delete: use shutil rmtree to remove all objects
216 216 """
217 217 paths = group.full_path.split(RepoGroup.url_sep())
218 218 paths = os.sep.join(paths)
219 219
220 220 rm_path = os.path.join(self.repos_path, paths)
221 221 log.info("Removing group %s", rm_path)
222 222 # delete only if that path really exists
223 223 if os.path.isdir(rm_path):
224 224 if force_delete:
225 225 shutil.rmtree(rm_path)
226 226 else:
227 227 # archive that group`
228 228 _now = datetime.datetime.now()
229 229 _ms = str(_now.microsecond).rjust(6, '0')
230 230 _d = 'rm__%s_GROUP_%s' % (
231 231 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
232 232 shutil.move(rm_path, os.path.join(self.repos_path, _d))
233 233
234 234 def create(self, group_name, group_description, owner, just_db=False,
235 235 copy_permissions=False, personal=None, commit_early=True):
236 236
237 237 (group_name_cleaned,
238 238 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
239 239
240 240 parent_group = None
241 241 if parent_group_name:
242 242 parent_group = self._get_repo_group(parent_group_name)
243 243 if not parent_group:
244 244 # we tried to create a nested group, but the parent is not
245 245 # existing
246 246 raise ValueError(
247 247 'Parent group `%s` given in `%s` group name '
248 248 'is not yet existing.' % (parent_group_name, group_name))
249 249
250 250 # because we are doing a cleanup, we need to check if such directory
251 251 # already exists. If we don't do that we can accidentally delete
252 252 # existing directory via cleanup that can cause data issues, since
253 253 # delete does a folder rename to special syntax later cleanup
254 254 # functions can delete this
255 255 cleanup_group = self.check_exist_filesystem(group_name,
256 256 exc_on_failure=False)
257 257 try:
258 258 user = self._get_user(owner)
259 259 new_repo_group = RepoGroup()
260 260 new_repo_group.user = user
261 261 new_repo_group.group_description = group_description or group_name
262 262 new_repo_group.parent_group = parent_group
263 263 new_repo_group.group_name = group_name
264 264 new_repo_group.personal = personal
265 265
266 266 self.sa.add(new_repo_group)
267 267
268 268 # create an ADMIN permission for owner except if we're super admin,
269 269 # later owner should go into the owner field of groups
270 270 if not user.is_admin:
271 271 self.grant_user_permission(repo_group=new_repo_group,
272 272 user=owner, perm='group.admin')
273 273
274 274 if parent_group and copy_permissions:
275 275 # copy permissions from parent
276 276 user_perms = UserRepoGroupToPerm.query() \
277 277 .filter(UserRepoGroupToPerm.group == parent_group).all()
278 278
279 279 group_perms = UserGroupRepoGroupToPerm.query() \
280 280 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
281 281
282 282 for perm in user_perms:
283 283 # don't copy over the permission for user who is creating
284 284 # this group, if he is not super admin he get's admin
285 285 # permission set above
286 286 if perm.user != user or user.is_admin:
287 287 UserRepoGroupToPerm.create(
288 288 perm.user, new_repo_group, perm.permission)
289 289
290 290 for perm in group_perms:
291 291 UserGroupRepoGroupToPerm.create(
292 292 perm.users_group, new_repo_group, perm.permission)
293 293 else:
294 294 perm_obj = self._create_default_perms(new_repo_group)
295 295 self.sa.add(perm_obj)
296 296
297 297 # now commit the changes, earlier so we are sure everything is in
298 298 # the database.
299 299 if commit_early:
300 300 self.sa.commit()
301 301 if not just_db:
302 302 self._create_group(new_repo_group.group_name)
303 303
304 304 # trigger the post hook
305 305 from rhodecode.lib.hooks_base import log_create_repository_group
306 306 repo_group = RepoGroup.get_by_group_name(group_name)
307 307 log_create_repository_group(
308 308 created_by=user.username, **repo_group.get_dict())
309 309
310 310 # Trigger create event.
311 311 events.trigger(events.RepoGroupCreateEvent(repo_group))
312 312
313 313 return new_repo_group
314 314 except Exception:
315 315 self.sa.rollback()
316 316 log.exception('Exception occurred when creating repository group, '
317 317 'doing cleanup...')
318 318 # rollback things manually !
319 319 repo_group = RepoGroup.get_by_group_name(group_name)
320 320 if repo_group:
321 321 RepoGroup.delete(repo_group.group_id)
322 322 self.sa.commit()
323 323 if cleanup_group:
324 324 RepoGroupModel()._delete_filesystem_group(repo_group)
325 325 raise
326 326
327 327 def update_permissions(
328 328 self, repo_group, perm_additions=None, perm_updates=None,
329 329 perm_deletions=None, recursive=None, check_perms=True,
330 330 cur_user=None):
331 331 from rhodecode.model.repo import RepoModel
332 332 from rhodecode.lib.auth import HasUserGroupPermissionAny
333 333
334 334 if not perm_additions:
335 335 perm_additions = []
336 336 if not perm_updates:
337 337 perm_updates = []
338 338 if not perm_deletions:
339 339 perm_deletions = []
340 340
341 341 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
342 342
343 changes = {
344 'added': [],
345 'updated': [],
346 'deleted': []
347 }
348
343 349 def _set_perm_user(obj, user, perm):
344 350 if isinstance(obj, RepoGroup):
345 351 self.grant_user_permission(
346 352 repo_group=obj, user=user, perm=perm)
347 353 elif isinstance(obj, Repository):
348 354 # private repos will not allow to change the default
349 355 # permissions using recursive mode
350 356 if obj.private and user == User.DEFAULT_USER:
351 357 return
352 358
353 359 # we set group permission but we have to switch to repo
354 360 # permission
355 361 perm = perm.replace('group.', 'repository.')
356 362 RepoModel().grant_user_permission(
357 363 repo=obj, user=user, perm=perm)
358 364
359 365 def _set_perm_group(obj, users_group, perm):
360 366 if isinstance(obj, RepoGroup):
361 367 self.grant_user_group_permission(
362 368 repo_group=obj, group_name=users_group, perm=perm)
363 369 elif isinstance(obj, Repository):
364 370 # we set group permission but we have to switch to repo
365 371 # permission
366 372 perm = perm.replace('group.', 'repository.')
367 373 RepoModel().grant_user_group_permission(
368 374 repo=obj, group_name=users_group, perm=perm)
369 375
370 376 def _revoke_perm_user(obj, user):
371 377 if isinstance(obj, RepoGroup):
372 378 self.revoke_user_permission(repo_group=obj, user=user)
373 379 elif isinstance(obj, Repository):
374 380 RepoModel().revoke_user_permission(repo=obj, user=user)
375 381
376 382 def _revoke_perm_group(obj, user_group):
377 383 if isinstance(obj, RepoGroup):
378 384 self.revoke_user_group_permission(
379 385 repo_group=obj, group_name=user_group)
380 386 elif isinstance(obj, Repository):
381 387 RepoModel().revoke_user_group_permission(
382 388 repo=obj, group_name=user_group)
383 389
384 390 # start updates
385 updates = []
386 391 log.debug('Now updating permissions for %s in recursive mode:%s',
387 392 repo_group, recursive)
388 393
389 394 # initialize check function, we'll call that multiple times
390 395 has_group_perm = HasUserGroupPermissionAny(*req_perms)
391 396
392 397 for obj in repo_group.recursive_groups_and_repos():
393 398 # iterated obj is an instance of a repos group or repository in
394 399 # that group, recursive option can be: none, repos, groups, all
395 400 if recursive == 'all':
396 401 obj = obj
397 402 elif recursive == 'repos':
398 403 # skip groups, other than this one
399 404 if isinstance(obj, RepoGroup) and not obj == repo_group:
400 405 continue
401 406 elif recursive == 'groups':
402 407 # skip repos
403 408 if isinstance(obj, Repository):
404 409 continue
405 410 else: # recursive == 'none':
406 411 # DEFAULT option - don't apply to iterated objects
407 412 # also we do a break at the end of this loop. if we are not
408 413 # in recursive mode
409 414 obj = repo_group
410 415
416 change_obj = obj.get_api_data()
417
411 418 # update permissions
412 419 for member_id, perm, member_type in perm_updates:
413 420 member_id = int(member_id)
414 421 if member_type == 'user':
422 member_name = User.get(member_id).username
415 423 # this updates also current one if found
416 424 _set_perm_user(obj, user=member_id, perm=perm)
417 425 else: # set for user group
418 426 member_name = UserGroup.get(member_id).users_group_name
419 427 if not check_perms or has_group_perm(member_name,
420 428 user=cur_user):
421 429 _set_perm_group(obj, users_group=member_id, perm=perm)
422 430
431 changes['updated'].append(
432 {'change_obj': change_obj, 'type': member_type,
433 'id': member_id, 'name': member_name, 'new_perm': perm})
434
423 435 # set new permissions
424 436 for member_id, perm, member_type in perm_additions:
425 437 member_id = int(member_id)
426 438 if member_type == 'user':
439 member_name = User.get(member_id).username
427 440 _set_perm_user(obj, user=member_id, perm=perm)
428 441 else: # set for user group
429 442 # check if we have permissions to alter this usergroup
430 443 member_name = UserGroup.get(member_id).users_group_name
431 444 if not check_perms or has_group_perm(member_name,
432 445 user=cur_user):
433 446 _set_perm_group(obj, users_group=member_id, perm=perm)
434 447
448 changes['added'].append(
449 {'change_obj': change_obj, 'type': member_type,
450 'id': member_id, 'name': member_name, 'new_perm': perm})
451
435 452 # delete permissions
436 453 for member_id, perm, member_type in perm_deletions:
437 454 member_id = int(member_id)
438 455 if member_type == 'user':
456 member_name = User.get(member_id).username
439 457 _revoke_perm_user(obj, user=member_id)
440 458 else: # set for user group
441 459 # check if we have permissions to alter this usergroup
442 460 member_name = UserGroup.get(member_id).users_group_name
443 461 if not check_perms or has_group_perm(member_name,
444 462 user=cur_user):
445 463 _revoke_perm_group(obj, user_group=member_id)
446 464
447 updates.append(obj)
465 changes['deleted'].append(
466 {'change_obj': change_obj, 'type': member_type,
467 'id': member_id, 'name': member_name, 'new_perm': perm})
468
448 469 # if it's not recursive call for all,repos,groups
449 470 # break the loop and don't proceed with other changes
450 471 if recursive not in ['all', 'repos', 'groups']:
451 472 break
452 473
453 return updates
474 return changes
454 475
455 476 def update(self, repo_group, form_data):
456 477 try:
457 478 repo_group = self._get_repo_group(repo_group)
458 479 old_path = repo_group.full_path
459 480
460 481 # change properties
461 482 if 'group_description' in form_data:
462 483 repo_group.group_description = form_data['group_description']
463 484
464 485 if 'enable_locking' in form_data:
465 486 repo_group.enable_locking = form_data['enable_locking']
466 487
467 488 if 'group_parent_id' in form_data:
468 489 parent_group = (
469 490 self._get_repo_group(form_data['group_parent_id']))
470 491 repo_group.group_parent_id = (
471 492 parent_group.group_id if parent_group else None)
472 493 repo_group.parent_group = parent_group
473 494
474 495 # mikhail: to update the full_path, we have to explicitly
475 496 # update group_name
476 497 group_name = form_data.get('group_name', repo_group.name)
477 498 repo_group.group_name = repo_group.get_new_name(group_name)
478 499
479 500 new_path = repo_group.full_path
480 501
481 502 if 'user' in form_data:
482 503 repo_group.user = User.get_by_username(form_data['user'])
483 504
484 505 self.sa.add(repo_group)
485 506
486 507 # iterate over all members of this groups and do fixes
487 508 # set locking if given
488 509 # if obj is a repoGroup also fix the name of the group according
489 510 # to the parent
490 511 # if obj is a Repo fix it's name
491 512 # this can be potentially heavy operation
492 513 for obj in repo_group.recursive_groups_and_repos():
493 514 # set the value from it's parent
494 515 obj.enable_locking = repo_group.enable_locking
495 516 if isinstance(obj, RepoGroup):
496 517 new_name = obj.get_new_name(obj.name)
497 518 log.debug('Fixing group %s to new name %s',
498 519 obj.group_name, new_name)
499 520 obj.group_name = new_name
500 521 elif isinstance(obj, Repository):
501 522 # we need to get all repositories from this new group and
502 523 # rename them accordingly to new group path
503 524 new_name = obj.get_new_name(obj.just_name)
504 525 log.debug('Fixing repo %s to new name %s',
505 526 obj.repo_name, new_name)
506 527 obj.repo_name = new_name
507 528 self.sa.add(obj)
508 529
509 530 self._rename_group(old_path, new_path)
510 531
511 532 # Trigger update event.
512 533 events.trigger(events.RepoGroupUpdateEvent(repo_group))
513 534
514 535 return repo_group
515 536 except Exception:
516 537 log.error(traceback.format_exc())
517 538 raise
518 539
519 540 def delete(self, repo_group, force_delete=False, fs_remove=True):
520 541 repo_group = self._get_repo_group(repo_group)
521 542 if not repo_group:
522 543 return False
523 544 try:
524 545 self.sa.delete(repo_group)
525 546 if fs_remove:
526 547 self._delete_filesystem_group(repo_group, force_delete)
527 548 else:
528 549 log.debug('skipping removal from filesystem')
529 550
530 551 # Trigger delete event.
531 552 events.trigger(events.RepoGroupDeleteEvent(repo_group))
532 553 return True
533 554
534 555 except Exception:
535 556 log.error('Error removing repo_group %s', repo_group)
536 557 raise
537 558
538 559 def grant_user_permission(self, repo_group, user, perm):
539 560 """
540 561 Grant permission for user on given repository group, or update
541 562 existing one if found
542 563
543 564 :param repo_group: Instance of RepoGroup, repositories_group_id,
544 565 or repositories_group name
545 566 :param user: Instance of User, user_id or username
546 567 :param perm: Instance of Permission, or permission_name
547 568 """
548 569
549 570 repo_group = self._get_repo_group(repo_group)
550 571 user = self._get_user(user)
551 572 permission = self._get_perm(perm)
552 573
553 574 # check if we have that permission already
554 575 obj = self.sa.query(UserRepoGroupToPerm)\
555 576 .filter(UserRepoGroupToPerm.user == user)\
556 577 .filter(UserRepoGroupToPerm.group == repo_group)\
557 578 .scalar()
558 579 if obj is None:
559 580 # create new !
560 581 obj = UserRepoGroupToPerm()
561 582 obj.group = repo_group
562 583 obj.user = user
563 584 obj.permission = permission
564 585 self.sa.add(obj)
565 586 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
566 587 action_logger_generic(
567 588 'granted permission: {} to user: {} on repogroup: {}'.format(
568 589 perm, user, repo_group), namespace='security.repogroup')
569 590 return obj
570 591
571 592 def revoke_user_permission(self, repo_group, user):
572 593 """
573 594 Revoke permission for user on given repository group
574 595
575 596 :param repo_group: Instance of RepoGroup, repositories_group_id,
576 597 or repositories_group name
577 598 :param user: Instance of User, user_id or username
578 599 """
579 600
580 601 repo_group = self._get_repo_group(repo_group)
581 602 user = self._get_user(user)
582 603
583 604 obj = self.sa.query(UserRepoGroupToPerm)\
584 605 .filter(UserRepoGroupToPerm.user == user)\
585 606 .filter(UserRepoGroupToPerm.group == repo_group)\
586 607 .scalar()
587 608 if obj:
588 609 self.sa.delete(obj)
589 610 log.debug('Revoked perm on %s on %s', repo_group, user)
590 611 action_logger_generic(
591 612 'revoked permission from user: {} on repogroup: {}'.format(
592 613 user, repo_group), namespace='security.repogroup')
593 614
594 615 def grant_user_group_permission(self, repo_group, group_name, perm):
595 616 """
596 617 Grant permission for user group on given repository group, or update
597 618 existing one if found
598 619
599 620 :param repo_group: Instance of RepoGroup, repositories_group_id,
600 621 or repositories_group name
601 622 :param group_name: Instance of UserGroup, users_group_id,
602 623 or user group name
603 624 :param perm: Instance of Permission, or permission_name
604 625 """
605 626 repo_group = self._get_repo_group(repo_group)
606 627 group_name = self._get_user_group(group_name)
607 628 permission = self._get_perm(perm)
608 629
609 630 # check if we have that permission already
610 631 obj = self.sa.query(UserGroupRepoGroupToPerm)\
611 632 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
612 633 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
613 634 .scalar()
614 635
615 636 if obj is None:
616 637 # create new
617 638 obj = UserGroupRepoGroupToPerm()
618 639
619 640 obj.group = repo_group
620 641 obj.users_group = group_name
621 642 obj.permission = permission
622 643 self.sa.add(obj)
623 644 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
624 645 action_logger_generic(
625 646 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
626 647 perm, group_name, repo_group), namespace='security.repogroup')
627 648 return obj
628 649
629 650 def revoke_user_group_permission(self, repo_group, group_name):
630 651 """
631 652 Revoke permission for user group on given repository group
632 653
633 654 :param repo_group: Instance of RepoGroup, repositories_group_id,
634 655 or repositories_group name
635 656 :param group_name: Instance of UserGroup, users_group_id,
636 657 or user group name
637 658 """
638 659 repo_group = self._get_repo_group(repo_group)
639 660 group_name = self._get_user_group(group_name)
640 661
641 662 obj = self.sa.query(UserGroupRepoGroupToPerm)\
642 663 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
643 664 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
644 665 .scalar()
645 666 if obj:
646 667 self.sa.delete(obj)
647 668 log.debug('Revoked perm to %s on %s', repo_group, group_name)
648 669 action_logger_generic(
649 670 'revoked permission from usergroup: {} on repogroup: {}'.format(
650 671 group_name, repo_group), namespace='security.repogroup')
651 672
652 673 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
653 674 super_user_actions=False):
654 675
655 676 from rhodecode.lib.utils import PartialRenderer
656 677 _render = PartialRenderer('data_table/_dt_elements.mako')
657 678 c = _render.c
658 679 h = _render.h
659 680
660 681 def quick_menu(repo_group_name):
661 682 return _render('quick_repo_group_menu', repo_group_name)
662 683
663 684 def repo_group_lnk(repo_group_name):
664 685 return _render('repo_group_name', repo_group_name)
665 686
666 687 def desc(desc, personal):
667 688 prefix = h.escaped_stylize(u'[personal] ') if personal else ''
668 689
669 690 if c.visual.stylify_metatags:
670 691 desc = h.urlify_text(prefix + h.escaped_stylize(desc))
671 692 else:
672 693 desc = h.urlify_text(prefix + h.html_escape(desc))
673 694
674 695 return _render('repo_group_desc', desc)
675 696
676 697 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
677 698 return _render(
678 699 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
679 700
680 701 def repo_group_name(repo_group_name, children_groups):
681 702 return _render("repo_group_name", repo_group_name, children_groups)
682 703
683 704 def user_profile(username):
684 705 return _render('user_profile', username)
685 706
686 707 repo_group_data = []
687 708 for group in repo_group_list:
688 709
689 710 row = {
690 711 "menu": quick_menu(group.group_name),
691 712 "name": repo_group_lnk(group.group_name),
692 713 "name_raw": group.group_name,
693 714 "desc": desc(group.group_description, group.personal),
694 715 "top_level_repos": 0,
695 716 "owner": user_profile(group.user.username)
696 717 }
697 718 if admin:
698 719 repo_count = group.repositories.count()
699 720 children_groups = map(
700 721 h.safe_unicode,
701 722 itertools.chain((g.name for g in group.parents),
702 723 (x.name for x in [group])))
703 724 row.update({
704 725 "action": repo_group_actions(
705 726 group.group_id, group.group_name, repo_count),
706 727 "top_level_repos": repo_count,
707 728 "name": repo_group_name(group.group_name, children_groups),
708 729
709 730 })
710 731 repo_group_data.append(row)
711 732
712 733 return repo_group_data
General Comments 0
You need to be logged in to leave comments. Login now