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