##// END OF EJS Templates
UserGroup on UserGroup permissions implementation....
marcink -
r3788:d9b89874 beta
parent child Browse files
Show More
@@ -1,368 +1,375 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users_groups
3 rhodecode.controllers.admin.users_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 User Groups crud controller for pylons
6 User Groups crud controller for pylons
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.exceptions import UserGroupsAssignedException
36 from rhodecode.lib.exceptions import UserGroupsAssignedException,\
37 RepoGroupAssignmentError
37 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
38 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
39 HasUserGroupPermissionAnyDecorator, HasPermissionAnyDecorator
40 HasUserGroupPermissionAnyDecorator, HasPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.model.scm import UserGroupList
42 from rhodecode.model.scm import UserGroupList
42 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
45 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
45 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
46 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
46 from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm,\
47 from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm,\
47 CustomDefaultPermissionsForm
48 CustomDefaultPermissionsForm
48 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
49 from rhodecode.lib.utils import action_logger
50 from rhodecode.lib.utils import action_logger
50 from sqlalchemy.orm import joinedload
51 from sqlalchemy.orm import joinedload
51 from webob.exc import HTTPInternalServerError
52 from webob.exc import HTTPInternalServerError
52
53
53 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
54
55
55
56
56 class UsersGroupsController(BaseController):
57 class UsersGroupsController(BaseController):
57 """REST Controller styled on the Atom Publishing Protocol"""
58 """REST Controller styled on the Atom Publishing Protocol"""
58 # To properly map this controller, ensure your config/routing.py
59 # To properly map this controller, ensure your config/routing.py
59 # file has a resource setup:
60 # file has a resource setup:
60 # map.resource('users_group', 'users_groups')
61 # map.resource('users_group', 'users_groups')
61
62
62 @LoginRequired()
63 @LoginRequired()
63 def __before__(self):
64 def __before__(self):
64 super(UsersGroupsController, self).__before__()
65 super(UsersGroupsController, self).__before__()
65 c.available_permissions = config['available_permissions']
66 c.available_permissions = config['available_permissions']
66
67
67 def __load_data(self, user_group_id):
68 def __load_data(self, user_group_id):
68 ugroup_repo_perms = UserGroupRepoToPerm.query()\
69 ugroup_repo_perms = UserGroupRepoToPerm.query()\
69 .options(joinedload(UserGroupRepoToPerm.permission))\
70 .options(joinedload(UserGroupRepoToPerm.permission))\
70 .options(joinedload(UserGroupRepoToPerm.repository))\
71 .options(joinedload(UserGroupRepoToPerm.repository))\
71 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
72 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
72 .all()
73 .all()
73
74
74 for gr in ugroup_repo_perms:
75 for gr in ugroup_repo_perms:
75 c.users_group.permissions['repositories'][gr.repository.repo_name] \
76 c.users_group.permissions['repositories'][gr.repository.repo_name] \
76 = gr.permission.permission_name
77 = gr.permission.permission_name
77
78
78 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
79 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
79 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
80 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
80 .options(joinedload(UserGroupRepoGroupToPerm.group))\
81 .options(joinedload(UserGroupRepoGroupToPerm.group))\
81 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
82 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
82 .all()
83 .all()
83
84
84 for gr in ugroup_group_perms:
85 for gr in ugroup_group_perms:
85 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
86 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
86 = gr.permission.permission_name
87 = gr.permission.permission_name
87
88
88 c.group_members_obj = sorted((x.user for x in c.users_group.members),
89 c.group_members_obj = sorted((x.user for x in c.users_group.members),
89 key=lambda u: u.username.lower())
90 key=lambda u: u.username.lower())
90
91
91 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
92 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
92 c.available_members = sorted(((x.user_id, x.username) for x in
93 c.available_members = sorted(((x.user_id, x.username) for x in
93 User.query().all()),
94 User.query().all()),
94 key=lambda u: u[1].lower())
95 key=lambda u: u[1].lower())
95 repo_model = RepoModel()
96 repo_model = RepoModel()
96 c.users_array = repo_model.get_users_js()
97 c.users_array = repo_model.get_users_js()
97
98 c.users_groups_array = repo_model.get_users_groups_js()
98 # commented out due to not now supporting assignment for user group
99 # on user group
100 c.users_groups_array = "[]" # repo_model.get_users_groups_js()
101 c.available_permissions = config['available_permissions']
99 c.available_permissions = config['available_permissions']
102
100
103 def __load_defaults(self, user_group_id):
101 def __load_defaults(self, user_group_id):
104 """
102 """
105 Load defaults settings for edit, and update
103 Load defaults settings for edit, and update
106
104
107 :param user_group_id:
105 :param user_group_id:
108 """
106 """
109 user_group = UserGroup.get_or_404(user_group_id)
107 user_group = UserGroup.get_or_404(user_group_id)
110 data = user_group.get_dict()
108 data = user_group.get_dict()
111
109
112 ug_model = UserGroupModel()
110 ug_model = UserGroupModel()
113
111
114 data.update({
112 data.update({
115 'create_repo_perm': ug_model.has_perm(user_group,
113 'create_repo_perm': ug_model.has_perm(user_group,
116 'hg.create.repository'),
114 'hg.create.repository'),
117 'create_user_group_perm': ug_model.has_perm(user_group,
115 'create_user_group_perm': ug_model.has_perm(user_group,
118 'hg.usergroup.create.true'),
116 'hg.usergroup.create.true'),
119 'fork_repo_perm': ug_model.has_perm(user_group,
117 'fork_repo_perm': ug_model.has_perm(user_group,
120 'hg.fork.repository'),
118 'hg.fork.repository'),
121 })
119 })
122
120
123 # fill user group users
121 # fill user group users
124 for p in user_group.user_user_group_to_perm:
122 for p in user_group.user_user_group_to_perm:
125 data.update({'u_perm_%s' % p.user.username:
123 data.update({'u_perm_%s' % p.user.username:
126 p.permission.permission_name})
124 p.permission.permission_name})
127
125
126 for p in user_group.user_group_user_group_to_perm:
127 data.update({'g_perm_%s' % p.user_group.users_group_name:
128 p.permission.permission_name})
129
128 return data
130 return data
129
131
130 def index(self, format='html'):
132 def index(self, format='html'):
131 """GET /users_groups: All items in the collection"""
133 """GET /users_groups: All items in the collection"""
132 # url('users_groups')
134 # url('users_groups')
133
135
134 group_iter = UserGroupList(UserGroup().query().all(),
136 group_iter = UserGroupList(UserGroup().query().all(),
135 perm_set=['usergroup.admin'])
137 perm_set=['usergroup.admin'])
136 sk = lambda g: g.users_group_name
138 sk = lambda g: g.users_group_name
137 c.users_groups_list = sorted(group_iter, key=sk)
139 c.users_groups_list = sorted(group_iter, key=sk)
138 return render('admin/users_groups/users_groups.html')
140 return render('admin/users_groups/users_groups.html')
139
141
140 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
142 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
141 def create(self):
143 def create(self):
142 """POST /users_groups: Create a new item"""
144 """POST /users_groups: Create a new item"""
143 # url('users_groups')
145 # url('users_groups')
144
146
145 users_group_form = UserGroupForm()()
147 users_group_form = UserGroupForm()()
146 try:
148 try:
147 form_result = users_group_form.to_python(dict(request.POST))
149 form_result = users_group_form.to_python(dict(request.POST))
148 UserGroupModel().create(name=form_result['users_group_name'],
150 UserGroupModel().create(name=form_result['users_group_name'],
149 owner=self.rhodecode_user.user_id,
151 owner=self.rhodecode_user.user_id,
150 active=form_result['users_group_active'])
152 active=form_result['users_group_active'])
151
153
152 gr = form_result['users_group_name']
154 gr = form_result['users_group_name']
153 action_logger(self.rhodecode_user,
155 action_logger(self.rhodecode_user,
154 'admin_created_users_group:%s' % gr,
156 'admin_created_users_group:%s' % gr,
155 None, self.ip_addr, self.sa)
157 None, self.ip_addr, self.sa)
156 h.flash(_('Created user group %s') % gr, category='success')
158 h.flash(_('Created user group %s') % gr, category='success')
157 Session().commit()
159 Session().commit()
158 except formencode.Invalid, errors:
160 except formencode.Invalid, errors:
159 return htmlfill.render(
161 return htmlfill.render(
160 render('admin/users_groups/users_group_add.html'),
162 render('admin/users_groups/users_group_add.html'),
161 defaults=errors.value,
163 defaults=errors.value,
162 errors=errors.error_dict or {},
164 errors=errors.error_dict or {},
163 prefix_error=False,
165 prefix_error=False,
164 encoding="UTF-8")
166 encoding="UTF-8")
165 except Exception:
167 except Exception:
166 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
167 h.flash(_('Error occurred during creation of user group %s') \
169 h.flash(_('Error occurred during creation of user group %s') \
168 % request.POST.get('users_group_name'), category='error')
170 % request.POST.get('users_group_name'), category='error')
169
171
170 return redirect(url('users_groups'))
172 return redirect(url('users_groups'))
171
173
172 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
174 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
173 def new(self, format='html'):
175 def new(self, format='html'):
174 """GET /users_groups/new: Form to create a new item"""
176 """GET /users_groups/new: Form to create a new item"""
175 # url('new_users_group')
177 # url('new_users_group')
176 return render('admin/users_groups/users_group_add.html')
178 return render('admin/users_groups/users_group_add.html')
177
179
178 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
180 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
179 def update(self, id):
181 def update(self, id):
180 """PUT /users_groups/id: Update an existing item"""
182 """PUT /users_groups/id: Update an existing item"""
181 # Forms posted to this method should contain a hidden field:
183 # Forms posted to this method should contain a hidden field:
182 # <input type="hidden" name="_method" value="PUT" />
184 # <input type="hidden" name="_method" value="PUT" />
183 # Or using helpers:
185 # Or using helpers:
184 # h.form(url('users_group', id=ID),
186 # h.form(url('users_group', id=ID),
185 # method='put')
187 # method='put')
186 # url('users_group', id=ID)
188 # url('users_group', id=ID)
187
189
188 c.users_group = UserGroup.get_or_404(id)
190 c.users_group = UserGroup.get_or_404(id)
189 self.__load_data(id)
191 self.__load_data(id)
190
192
191 available_members = [safe_unicode(x[0]) for x in c.available_members]
193 available_members = [safe_unicode(x[0]) for x in c.available_members]
192
194
193 users_group_form = UserGroupForm(edit=True,
195 users_group_form = UserGroupForm(edit=True,
194 old_data=c.users_group.get_dict(),
196 old_data=c.users_group.get_dict(),
195 available_members=available_members)()
197 available_members=available_members)()
196
198
197 try:
199 try:
198 form_result = users_group_form.to_python(request.POST)
200 form_result = users_group_form.to_python(request.POST)
199 UserGroupModel().update(c.users_group, form_result)
201 UserGroupModel().update(c.users_group, form_result)
200 gr = form_result['users_group_name']
202 gr = form_result['users_group_name']
201 action_logger(self.rhodecode_user,
203 action_logger(self.rhodecode_user,
202 'admin_updated_users_group:%s' % gr,
204 'admin_updated_users_group:%s' % gr,
203 None, self.ip_addr, self.sa)
205 None, self.ip_addr, self.sa)
204 h.flash(_('Updated user group %s') % gr, category='success')
206 h.flash(_('Updated user group %s') % gr, category='success')
205 Session().commit()
207 Session().commit()
206 except formencode.Invalid, errors:
208 except formencode.Invalid, errors:
207 ug_model = UserGroupModel()
209 ug_model = UserGroupModel()
208 defaults = errors.value
210 defaults = errors.value
209 e = errors.error_dict or {}
211 e = errors.error_dict or {}
210 defaults.update({
212 defaults.update({
211 'create_repo_perm': ug_model.has_perm(id,
213 'create_repo_perm': ug_model.has_perm(id,
212 'hg.create.repository'),
214 'hg.create.repository'),
213 'fork_repo_perm': ug_model.has_perm(id,
215 'fork_repo_perm': ug_model.has_perm(id,
214 'hg.fork.repository'),
216 'hg.fork.repository'),
215 '_method': 'put'
217 '_method': 'put'
216 })
218 })
217
219
218 return htmlfill.render(
220 return htmlfill.render(
219 render('admin/users_groups/users_group_edit.html'),
221 render('admin/users_groups/users_group_edit.html'),
220 defaults=defaults,
222 defaults=defaults,
221 errors=e,
223 errors=e,
222 prefix_error=False,
224 prefix_error=False,
223 encoding="UTF-8")
225 encoding="UTF-8")
224 except Exception:
226 except Exception:
225 log.error(traceback.format_exc())
227 log.error(traceback.format_exc())
226 h.flash(_('Error occurred during update of user group %s') \
228 h.flash(_('Error occurred during update of user group %s') \
227 % request.POST.get('users_group_name'), category='error')
229 % request.POST.get('users_group_name'), category='error')
228
230
229 return redirect(url('edit_users_group', id=id))
231 return redirect(url('edit_users_group', id=id))
230
232
231 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
233 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
232 def delete(self, id):
234 def delete(self, id):
233 """DELETE /users_groups/id: Delete an existing item"""
235 """DELETE /users_groups/id: Delete an existing item"""
234 # Forms posted to this method should contain a hidden field:
236 # Forms posted to this method should contain a hidden field:
235 # <input type="hidden" name="_method" value="DELETE" />
237 # <input type="hidden" name="_method" value="DELETE" />
236 # Or using helpers:
238 # Or using helpers:
237 # h.form(url('users_group', id=ID),
239 # h.form(url('users_group', id=ID),
238 # method='delete')
240 # method='delete')
239 # url('users_group', id=ID)
241 # url('users_group', id=ID)
240 usr_gr = UserGroup.get_or_404(id)
242 usr_gr = UserGroup.get_or_404(id)
241 try:
243 try:
242 UserGroupModel().delete(usr_gr)
244 UserGroupModel().delete(usr_gr)
243 Session().commit()
245 Session().commit()
244 h.flash(_('Successfully deleted user group'), category='success')
246 h.flash(_('Successfully deleted user group'), category='success')
245 except UserGroupsAssignedException, e:
247 except UserGroupsAssignedException, e:
246 h.flash(e, category='error')
248 h.flash(e, category='error')
247 except Exception:
249 except Exception:
248 log.error(traceback.format_exc())
250 log.error(traceback.format_exc())
249 h.flash(_('An error occurred during deletion of user group'),
251 h.flash(_('An error occurred during deletion of user group'),
250 category='error')
252 category='error')
251 return redirect(url('users_groups'))
253 return redirect(url('users_groups'))
252
254
253 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
255 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
254 def set_user_group_perm_member(self, id):
256 def set_user_group_perm_member(self, id):
255 """
257 """
256 grant permission for given usergroup
258 grant permission for given usergroup
257
259
258 :param id:
260 :param id:
259 """
261 """
260 user_group = UserGroup.get_or_404(id)
262 user_group = UserGroup.get_or_404(id)
261 form = UserGroupPermsForm()().to_python(request.POST)
263 form = UserGroupPermsForm()().to_python(request.POST)
262
264
263 # set the permissions !
265 # set the permissions !
264 UserGroupModel()._update_permissions(user_group, form['perms_new'],
266 try:
265 form['perms_updates'])
267 UserGroupModel()._update_permissions(user_group, form['perms_new'],
268 form['perms_updates'])
269 except RepoGroupAssignmentError:
270 h.flash(_('Target group cannot be the same'), category='error')
271 return redirect(url('edit_users_group', id=id))
266 #TODO: implement this
272 #TODO: implement this
267 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
273 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
268 # repo_name, self.ip_addr, self.sa)
274 # repo_name, self.ip_addr, self.sa)
269 Session().commit()
275 Session().commit()
270 h.flash(_('User Group permissions updated'), category='success')
276 h.flash(_('User Group permissions updated'), category='success')
271 return redirect(url('edit_users_group', id=id))
277 return redirect(url('edit_users_group', id=id))
272
278
273 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
279 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
274 def delete_user_group_perm_member(self, id):
280 def delete_user_group_perm_member(self, id):
275 """
281 """
276 DELETE an existing repository group permission user
282 DELETE an existing repository group permission user
277
283
278 :param group_name:
284 :param group_name:
279 """
285 """
280 try:
286 try:
281 obj_type = request.POST.get('obj_type')
287 obj_type = request.POST.get('obj_type')
282 obj_id = None
288 obj_id = None
283 if obj_type == 'user':
289 if obj_type == 'user':
284 obj_id = safe_int(request.POST.get('user_id'))
290 obj_id = safe_int(request.POST.get('user_id'))
285 elif obj_type == 'user_group':
291 elif obj_type == 'user_group':
286 obj_id = safe_int(request.POST.get('user_group_id'))
292 obj_id = safe_int(request.POST.get('user_group_id'))
287
293
288 if not c.rhodecode_user.is_admin:
294 if not c.rhodecode_user.is_admin:
289 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
295 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
290 msg = _('Cannot revoke permission for yourself as admin')
296 msg = _('Cannot revoke permission for yourself as admin')
291 h.flash(msg, category='warning')
297 h.flash(msg, category='warning')
292 raise Exception('revoke admin permission on self')
298 raise Exception('revoke admin permission on self')
293 if obj_type == 'user':
299 if obj_type == 'user':
294 UserGroupModel().revoke_user_permission(user_group=id,
300 UserGroupModel().revoke_user_permission(user_group=id,
295 user=obj_id)
301 user=obj_id)
296 elif obj_type == 'user_group':
302 elif obj_type == 'user_group':
297 pass
303 UserGroupModel().revoke_users_group_permission(target_user_group=id,
304 user_group=obj_id)
298 Session().commit()
305 Session().commit()
299 except Exception:
306 except Exception:
300 log.error(traceback.format_exc())
307 log.error(traceback.format_exc())
301 h.flash(_('An error occurred during revoking of permission'),
308 h.flash(_('An error occurred during revoking of permission'),
302 category='error')
309 category='error')
303 raise HTTPInternalServerError()
310 raise HTTPInternalServerError()
304
311
305 def show(self, id, format='html'):
312 def show(self, id, format='html'):
306 """GET /users_groups/id: Show a specific item"""
313 """GET /users_groups/id: Show a specific item"""
307 # url('users_group', id=ID)
314 # url('users_group', id=ID)
308
315
309 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
316 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
310 def edit(self, id, format='html'):
317 def edit(self, id, format='html'):
311 """GET /users_groups/id/edit: Form to edit an existing item"""
318 """GET /users_groups/id/edit: Form to edit an existing item"""
312 # url('edit_users_group', id=ID)
319 # url('edit_users_group', id=ID)
313
320
314 c.users_group = UserGroup.get_or_404(id)
321 c.users_group = UserGroup.get_or_404(id)
315 self.__load_data(id)
322 self.__load_data(id)
316
323
317 defaults = self.__load_defaults(id)
324 defaults = self.__load_defaults(id)
318
325
319 return htmlfill.render(
326 return htmlfill.render(
320 render('admin/users_groups/users_group_edit.html'),
327 render('admin/users_groups/users_group_edit.html'),
321 defaults=defaults,
328 defaults=defaults,
322 encoding="UTF-8",
329 encoding="UTF-8",
323 force_defaults=False
330 force_defaults=False
324 )
331 )
325
332
326 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
333 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
327 def update_perm(self, id):
334 def update_perm(self, id):
328 """PUT /users_perm/id: Update an existing item"""
335 """PUT /users_perm/id: Update an existing item"""
329 # url('users_group_perm', id=ID, method='put')
336 # url('users_group_perm', id=ID, method='put')
330
337
331 users_group = UserGroup.get_or_404(id)
338 users_group = UserGroup.get_or_404(id)
332
339
333 try:
340 try:
334 form = CustomDefaultPermissionsForm()()
341 form = CustomDefaultPermissionsForm()()
335 form_result = form.to_python(request.POST)
342 form_result = form.to_python(request.POST)
336
343
337 inherit_perms = form_result['inherit_default_permissions']
344 inherit_perms = form_result['inherit_default_permissions']
338 users_group.inherit_default_permissions = inherit_perms
345 users_group.inherit_default_permissions = inherit_perms
339 Session().add(users_group)
346 Session().add(users_group)
340 usergroup_model = UserGroupModel()
347 usergroup_model = UserGroupModel()
341
348
342 defs = UserGroupToPerm.query()\
349 defs = UserGroupToPerm.query()\
343 .filter(UserGroupToPerm.users_group == users_group)\
350 .filter(UserGroupToPerm.users_group == users_group)\
344 .all()
351 .all()
345 for ug in defs:
352 for ug in defs:
346 Session().delete(ug)
353 Session().delete(ug)
347
354
348 if form_result['create_repo_perm']:
355 if form_result['create_repo_perm']:
349 usergroup_model.grant_perm(id, 'hg.create.repository')
356 usergroup_model.grant_perm(id, 'hg.create.repository')
350 else:
357 else:
351 usergroup_model.grant_perm(id, 'hg.create.none')
358 usergroup_model.grant_perm(id, 'hg.create.none')
352 if form_result['create_user_group_perm']:
359 if form_result['create_user_group_perm']:
353 usergroup_model.grant_perm(id, 'hg.usergroup.create.true')
360 usergroup_model.grant_perm(id, 'hg.usergroup.create.true')
354 else:
361 else:
355 usergroup_model.grant_perm(id, 'hg.usergroup.create.false')
362 usergroup_model.grant_perm(id, 'hg.usergroup.create.false')
356 if form_result['fork_repo_perm']:
363 if form_result['fork_repo_perm']:
357 usergroup_model.grant_perm(id, 'hg.fork.repository')
364 usergroup_model.grant_perm(id, 'hg.fork.repository')
358 else:
365 else:
359 usergroup_model.grant_perm(id, 'hg.fork.none')
366 usergroup_model.grant_perm(id, 'hg.fork.none')
360
367
361 h.flash(_("Updated permissions"), category='success')
368 h.flash(_("Updated permissions"), category='success')
362 Session().commit()
369 Session().commit()
363 except Exception:
370 except Exception:
364 log.error(traceback.format_exc())
371 log.error(traceback.format_exc())
365 h.flash(_('An error occurred during permissions saving'),
372 h.flash(_('An error occurred during permissions saving'),
366 category='error')
373 category='error')
367
374
368 return redirect(url('edit_users_group', id=id))
375 return redirect(url('edit_users_group', id=id))
@@ -1,80 +1,84 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.exceptions
3 rhodecode.lib.exceptions
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Set of custom exceptions used in RhodeCode
6 Set of custom exceptions used in RhodeCode
7
7
8 :created_on: Nov 17, 2010
8 :created_on: Nov 17, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 from webob.exc import HTTPClientError
26 from webob.exc import HTTPClientError
27
27
28
28
29 class LdapUsernameError(Exception):
29 class LdapUsernameError(Exception):
30 pass
30 pass
31
31
32
32
33 class LdapPasswordError(Exception):
33 class LdapPasswordError(Exception):
34 pass
34 pass
35
35
36
36
37 class LdapConnectionError(Exception):
37 class LdapConnectionError(Exception):
38 pass
38 pass
39
39
40
40
41 class LdapImportError(Exception):
41 class LdapImportError(Exception):
42 pass
42 pass
43
43
44
44
45 class DefaultUserException(Exception):
45 class DefaultUserException(Exception):
46 pass
46 pass
47
47
48
48
49 class UserOwnsReposException(Exception):
49 class UserOwnsReposException(Exception):
50 pass
50 pass
51
51
52
52
53 class UserGroupsAssignedException(Exception):
53 class UserGroupsAssignedException(Exception):
54 pass
54 pass
55
55
56
56
57 class StatusChangeOnClosedPullRequestError(Exception):
57 class StatusChangeOnClosedPullRequestError(Exception):
58 pass
58 pass
59
59
60
60
61 class AttachedForksError(Exception):
61 class AttachedForksError(Exception):
62 pass
62 pass
63
63
64
64
65 class RepoGroupAssignmentError(Exception):
66 pass
67
68
65 class HTTPLockedRC(HTTPClientError):
69 class HTTPLockedRC(HTTPClientError):
66 """
70 """
67 Special Exception For locked Repos in RhodeCode, the return code can
71 Special Exception For locked Repos in RhodeCode, the return code can
68 be overwritten by _code keyword argument passed into constructors
72 be overwritten by _code keyword argument passed into constructors
69 """
73 """
70 code = 423
74 code = 423
71 title = explanation = 'Repository Locked'
75 title = explanation = 'Repository Locked'
72
76
73 def __init__(self, reponame, username, *args, **kwargs):
77 def __init__(self, reponame, username, *args, **kwargs):
74 from rhodecode import CONFIG
78 from rhodecode import CONFIG
75 from rhodecode.lib.utils2 import safe_int
79 from rhodecode.lib.utils2 import safe_int
76 _code = CONFIG.get('lock_ret_code')
80 _code = CONFIG.get('lock_ret_code')
77 self.code = safe_int(_code, self.code)
81 self.code = safe_int(_code, self.code)
78 self.title = self.explanation = ('Repository `%s` locked by '
82 self.title = self.explanation = ('Repository `%s` locked by '
79 'user `%s`' % (reponame, username))
83 'user `%s`' % (reponame, username))
80 super(HTTPLockedRC, self).__init__(*args, **kwargs)
84 super(HTTPLockedRC, self).__init__(*args, **kwargs)
@@ -1,93 +1,94 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 vcs.exceptions
3 vcs.exceptions
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 Custom exceptions module
6 Custom exceptions module
7
7
8 :created_on: Apr 8, 2010
8 :created_on: Apr 8, 2010
9 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
9 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
10 """
10 """
11
11
12
12
13 class VCSError(Exception):
13 class VCSError(Exception):
14 pass
14 pass
15
15
16
16
17 class RepositoryError(VCSError):
17 class RepositoryError(VCSError):
18 pass
18 pass
19
19
20
20
21 class EmptyRepositoryError(RepositoryError):
21 class EmptyRepositoryError(RepositoryError):
22 pass
22 pass
23
23
24
24
25 class TagAlreadyExistError(RepositoryError):
25 class TagAlreadyExistError(RepositoryError):
26 pass
26 pass
27
27
28
28
29 class TagDoesNotExistError(RepositoryError):
29 class TagDoesNotExistError(RepositoryError):
30 pass
30 pass
31
31
32
32
33 class BranchAlreadyExistError(RepositoryError):
33 class BranchAlreadyExistError(RepositoryError):
34 pass
34 pass
35
35
36
36
37 class BranchDoesNotExistError(RepositoryError):
37 class BranchDoesNotExistError(RepositoryError):
38 pass
38 pass
39
39
40
40
41 class ChangesetError(RepositoryError):
41 class ChangesetError(RepositoryError):
42 pass
42 pass
43
43
44
44
45 class ChangesetDoesNotExistError(ChangesetError):
45 class ChangesetDoesNotExistError(ChangesetError):
46 pass
46 pass
47
47
48
48
49 class CommitError(RepositoryError):
49 class CommitError(RepositoryError):
50 pass
50 pass
51
51
52
52
53 class NothingChangedError(CommitError):
53 class NothingChangedError(CommitError):
54 pass
54 pass
55
55
56
56
57 class NodeError(VCSError):
57 class NodeError(VCSError):
58 pass
58 pass
59
59
60
60
61 class RemovedFileNodeError(NodeError):
61 class RemovedFileNodeError(NodeError):
62 pass
62 pass
63
63
64
64
65 class NodeAlreadyExistsError(CommitError):
65 class NodeAlreadyExistsError(CommitError):
66 pass
66 pass
67
67
68
68
69 class NodeAlreadyChangedError(CommitError):
69 class NodeAlreadyChangedError(CommitError):
70 pass
70 pass
71
71
72
72
73 class NodeDoesNotExistError(CommitError):
73 class NodeDoesNotExistError(CommitError):
74 pass
74 pass
75
75
76
76
77 class NodeNotChangedError(CommitError):
77 class NodeNotChangedError(CommitError):
78 pass
78 pass
79
79
80
80
81 class NodeAlreadyAddedError(CommitError):
81 class NodeAlreadyAddedError(CommitError):
82 pass
82 pass
83
83
84
84
85 class NodeAlreadyRemovedError(CommitError):
85 class NodeAlreadyRemovedError(CommitError):
86 pass
86 pass
87
87
88
88
89 class ImproperArchiveTypeError(VCSError):
89 class ImproperArchiveTypeError(VCSError):
90 pass
90 pass
91
91
92
92 class CommandError(VCSError):
93 class CommandError(VCSError):
93 pass
94 pass
@@ -1,2129 +1,2131 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 @classmethod
273 @classmethod
274 def get_by_key(cls, key):
274 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
275 return cls.query().filter(cls.ui_key == key).scalar()
276
276
277 @classmethod
277 @classmethod
278 def get_builtin_hooks(cls):
278 def get_builtin_hooks(cls):
279 q = cls.query()
279 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
283 return q.all()
284
284
285 @classmethod
285 @classmethod
286 def get_custom_hooks(cls):
286 def get_custom_hooks(cls):
287 q = cls.query()
287 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
291 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
292 return q.all()
293
293
294 @classmethod
294 @classmethod
295 def get_repos_location(cls):
295 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
296 return cls.get_by_key('/').ui_value
297
297
298 @classmethod
298 @classmethod
299 def create_or_update_hook(cls, key, val):
299 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
300 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
301 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
302 new_ui.ui_active = True
303 new_ui.ui_key = key
303 new_ui.ui_key = key
304 new_ui.ui_value = val
304 new_ui.ui_value = val
305
305
306 Session().add(new_ui)
306 Session().add(new_ui)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
310 self.ui_value)
311
311
312
312
313 class User(Base, BaseModel):
313 class User(Base, BaseModel):
314 __tablename__ = 'users'
314 __tablename__ = 'users'
315 __table_args__ = (
315 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
316 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
317 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
318 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
320 'mysql_charset': 'utf8'}
321 )
321 )
322 DEFAULT_USER = 'default'
322 DEFAULT_USER = 'default'
323
323
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
336
337 user_log = relationship('UserLog')
337 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
339
340 repositories = relationship('Repository')
340 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
343
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
346
347 group_member = relationship('UserGroupMember', cascade='all')
347 group_member = relationship('UserGroupMember', cascade='all')
348
348
349 notifications = relationship('UserNotification', cascade='all')
349 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
350 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
351 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
352 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
353 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
354 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
355 user_emails = relationship('UserEmailMap', cascade='all')
356
356
357 @hybrid_property
357 @hybrid_property
358 def email(self):
358 def email(self):
359 return self._email
359 return self._email
360
360
361 @email.setter
361 @email.setter
362 def email(self, val):
362 def email(self, val):
363 self._email = val.lower() if val else None
363 self._email = val.lower() if val else None
364
364
365 @property
365 @property
366 def firstname(self):
366 def firstname(self):
367 # alias for future
367 # alias for future
368 return self.name
368 return self.name
369
369
370 @property
370 @property
371 def emails(self):
371 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
373 return [self.email] + [x.email for x in other]
374
374
375 @property
375 @property
376 def ip_addresses(self):
376 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
378 return [x.ip_addr for x in ret]
379
379
380 @property
380 @property
381 def username_and_name(self):
381 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
383
384 @property
384 @property
385 def full_name(self):
385 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
386 return '%s %s' % (self.firstname, self.lastname)
387
387
388 @property
388 @property
389 def full_name_or_username(self):
389 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
390 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
391 if (self.firstname and self.lastname) else self.username)
392
392
393 @property
393 @property
394 def full_contact(self):
394 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
396
397 @property
397 @property
398 def short_contact(self):
398 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
399 return '%s %s' % (self.firstname, self.lastname)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def AuthUser(self):
406 def AuthUser(self):
407 """
407 """
408 Returns instance of AuthUser for this user
408 Returns instance of AuthUser for this user
409 """
409 """
410 from rhodecode.lib.auth import AuthUser
410 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
412 username=self.username)
413
413
414 def __unicode__(self):
414 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
416 self.user_id, self.username)
417
417
418 @classmethod
418 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
420 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
421 q = cls.query().filter(cls.username.ilike(username))
422 else:
422 else:
423 q = cls.query().filter(cls.username == username)
423 q = cls.query().filter(cls.username == username)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache(
426 q = q.options(FromCache(
427 "sql_cache_short",
427 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
428 "get_user_%s" % _hash_key(username)
429 )
429 )
430 )
430 )
431 return q.scalar()
431 return q.scalar()
432
432
433 @classmethod
433 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
434 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
435 q = cls.query().filter(cls.api_key == api_key)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
439 "get_api_key_%s" % api_key))
440 return q.scalar()
440 return q.scalar()
441
441
442 @classmethod
442 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
444 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
445 q = cls.query().filter(cls.email.ilike(email))
446 else:
446 else:
447 q = cls.query().filter(cls.email == email)
447 q = cls.query().filter(cls.email == email)
448
448
449 if cache:
449 if cache:
450 q = q.options(FromCache("sql_cache_short",
450 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
451 "get_email_key_%s" % email))
452
452
453 ret = q.scalar()
453 ret = q.scalar()
454 if ret is None:
454 if ret is None:
455 q = UserEmailMap.query()
455 q = UserEmailMap.query()
456 # try fetching in alternate email map
456 # try fetching in alternate email map
457 if case_insensitive:
457 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
458 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
459 else:
460 q = q.filter(UserEmailMap.email == email)
460 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
461 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
462 if cache:
463 q = q.options(FromCache("sql_cache_short",
463 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
464 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
465 ret = getattr(q.scalar(), 'user', None)
466
466
467 return ret
467 return ret
468
468
469 @classmethod
469 @classmethod
470 def get_from_cs_author(cls, author):
470 def get_from_cs_author(cls, author):
471 """
471 """
472 Tries to get User objects out of commit author string
472 Tries to get User objects out of commit author string
473
473
474 :param author:
474 :param author:
475 """
475 """
476 from rhodecode.lib.helpers import email, author_name
476 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
477 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
478 _email = email(author)
479 if _email:
479 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
480 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
481 if user:
482 return user
482 return user
483 # Maybe we can match by username?
483 # Maybe we can match by username?
484 _author = author_name(author)
484 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
485 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
486 if user:
487 return user
487 return user
488
488
489 def update_lastlogin(self):
489 def update_lastlogin(self):
490 """Update user lastlogin"""
490 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
491 self.last_login = datetime.datetime.now()
492 Session().add(self)
492 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
493 log.debug('updated user %s lastlogin' % self.username)
494
494
495 @classmethod
495 @classmethod
496 def get_first_admin(cls):
496 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
497 user = User.query().filter(User.admin == True).first()
498 if user is None:
498 if user is None:
499 raise Exception('Missing administrative account!')
499 raise Exception('Missing administrative account!')
500 return user
500 return user
501
501
502 @classmethod
502 @classmethod
503 def get_default_user(cls, cache=False):
503 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
505 if user is None:
506 raise Exception('Missing default account!')
506 raise Exception('Missing default account!')
507 return user
507 return user
508
508
509 def get_api_data(self):
509 def get_api_data(self):
510 """
510 """
511 Common function for generating user related data for API
511 Common function for generating user related data for API
512 """
512 """
513 user = self
513 user = self
514 data = dict(
514 data = dict(
515 user_id=user.user_id,
515 user_id=user.user_id,
516 username=user.username,
516 username=user.username,
517 firstname=user.name,
517 firstname=user.name,
518 lastname=user.lastname,
518 lastname=user.lastname,
519 email=user.email,
519 email=user.email,
520 emails=user.emails,
520 emails=user.emails,
521 api_key=user.api_key,
521 api_key=user.api_key,
522 active=user.active,
522 active=user.active,
523 admin=user.admin,
523 admin=user.admin,
524 ldap_dn=user.ldap_dn,
524 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
525 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
526 ip_addresses=user.ip_addresses
527 )
527 )
528 return data
528 return data
529
529
530 def __json__(self):
530 def __json__(self):
531 data = dict(
531 data = dict(
532 full_name=self.full_name,
532 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
533 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
534 short_contact=self.short_contact,
535 full_contact=self.full_contact
535 full_contact=self.full_contact
536 )
536 )
537 data.update(self.get_api_data())
537 data.update(self.get_api_data())
538 return data
538 return data
539
539
540
540
541 class UserEmailMap(Base, BaseModel):
541 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
542 __tablename__ = 'user_email_map'
543 __table_args__ = (
543 __table_args__ = (
544 Index('uem_email_idx', 'email'),
544 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
545 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
547 'mysql_charset': 'utf8'}
548 )
548 )
549 __mapper_args__ = {}
549 __mapper_args__ = {}
550
550
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
554 user = relationship('User', lazy='joined')
555
555
556 @validates('_email')
556 @validates('_email')
557 def validate_email(self, key, email):
557 def validate_email(self, key, email):
558 # check if this email is not main one
558 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
559 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
560 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
561 raise AttributeError('email %s is present is user table' % email)
562 return email
562 return email
563
563
564 @hybrid_property
564 @hybrid_property
565 def email(self):
565 def email(self):
566 return self._email
566 return self._email
567
567
568 @email.setter
568 @email.setter
569 def email(self, val):
569 def email(self, val):
570 self._email = val.lower() if val else None
570 self._email = val.lower() if val else None
571
571
572
572
573 class UserIpMap(Base, BaseModel):
573 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
574 __tablename__ = 'user_ip_map'
575 __table_args__ = (
575 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
576 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
578 'mysql_charset': 'utf8'}
579 )
579 )
580 __mapper_args__ = {}
580 __mapper_args__ = {}
581
581
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
586 user = relationship('User', lazy='joined')
587
587
588 @classmethod
588 @classmethod
589 def _get_ip_range(cls, ip_addr):
589 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
590 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
591 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
592 return [str(net.network), str(net.broadcast)]
593
593
594 def __json__(self):
594 def __json__(self):
595 return dict(
595 return dict(
596 ip_addr=self.ip_addr,
596 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
597 ip_range=self._get_ip_range(self.ip_addr)
598 )
598 )
599
599
600
600
601 class UserLog(Base, BaseModel):
601 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
602 __tablename__ = 'user_logs'
603 __table_args__ = (
603 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
605 'mysql_charset': 'utf8'},
606 )
606 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
615
616 @property
616 @property
617 def action_as_day(self):
617 def action_as_day(self):
618 return datetime.date(*self.action_date.timetuple()[:3])
618 return datetime.date(*self.action_date.timetuple()[:3])
619
619
620 user = relationship('User')
620 user = relationship('User')
621 repository = relationship('Repository', cascade='')
621 repository = relationship('Repository', cascade='')
622
622
623
623
624 class UserGroup(Base, BaseModel):
624 class UserGroup(Base, BaseModel):
625 __tablename__ = 'users_groups'
625 __tablename__ = 'users_groups'
626 __table_args__ = (
626 __table_args__ = (
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
628 'mysql_charset': 'utf8'},
628 'mysql_charset': 'utf8'},
629 )
629 )
630
630
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636
636
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
642 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
643
642 user = relationship('User')
644 user = relationship('User')
643
645
644 def __unicode__(self):
646 def __unicode__(self):
645 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
646 self.users_group_id,
648 self.users_group_id,
647 self.users_group_name)
649 self.users_group_name)
648
650
649 @classmethod
651 @classmethod
650 def get_by_group_name(cls, group_name, cache=False,
652 def get_by_group_name(cls, group_name, cache=False,
651 case_insensitive=False):
653 case_insensitive=False):
652 if case_insensitive:
654 if case_insensitive:
653 q = cls.query().filter(cls.users_group_name.ilike(group_name))
655 q = cls.query().filter(cls.users_group_name.ilike(group_name))
654 else:
656 else:
655 q = cls.query().filter(cls.users_group_name == group_name)
657 q = cls.query().filter(cls.users_group_name == group_name)
656 if cache:
658 if cache:
657 q = q.options(FromCache(
659 q = q.options(FromCache(
658 "sql_cache_short",
660 "sql_cache_short",
659 "get_user_%s" % _hash_key(group_name)
661 "get_user_%s" % _hash_key(group_name)
660 )
662 )
661 )
663 )
662 return q.scalar()
664 return q.scalar()
663
665
664 @classmethod
666 @classmethod
665 def get(cls, users_group_id, cache=False):
667 def get(cls, users_group_id, cache=False):
666 users_group = cls.query()
668 users_group = cls.query()
667 if cache:
669 if cache:
668 users_group = users_group.options(FromCache("sql_cache_short",
670 users_group = users_group.options(FromCache("sql_cache_short",
669 "get_users_group_%s" % users_group_id))
671 "get_users_group_%s" % users_group_id))
670 return users_group.get(users_group_id)
672 return users_group.get(users_group_id)
671
673
672 def get_api_data(self):
674 def get_api_data(self):
673 users_group = self
675 users_group = self
674
676
675 data = dict(
677 data = dict(
676 users_group_id=users_group.users_group_id,
678 users_group_id=users_group.users_group_id,
677 group_name=users_group.users_group_name,
679 group_name=users_group.users_group_name,
678 active=users_group.users_group_active,
680 active=users_group.users_group_active,
679 )
681 )
680
682
681 return data
683 return data
682
684
683
685
684 class UserGroupMember(Base, BaseModel):
686 class UserGroupMember(Base, BaseModel):
685 __tablename__ = 'users_groups_members'
687 __tablename__ = 'users_groups_members'
686 __table_args__ = (
688 __table_args__ = (
687 {'extend_existing': True, 'mysql_engine': 'InnoDB',
689 {'extend_existing': True, 'mysql_engine': 'InnoDB',
688 'mysql_charset': 'utf8'},
690 'mysql_charset': 'utf8'},
689 )
691 )
690
692
691 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
693 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
692 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
694 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
693 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
695 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
694
696
695 user = relationship('User', lazy='joined')
697 user = relationship('User', lazy='joined')
696 users_group = relationship('UserGroup')
698 users_group = relationship('UserGroup')
697
699
698 def __init__(self, gr_id='', u_id=''):
700 def __init__(self, gr_id='', u_id=''):
699 self.users_group_id = gr_id
701 self.users_group_id = gr_id
700 self.user_id = u_id
702 self.user_id = u_id
701
703
702
704
703 class RepositoryField(Base, BaseModel):
705 class RepositoryField(Base, BaseModel):
704 __tablename__ = 'repositories_fields'
706 __tablename__ = 'repositories_fields'
705 __table_args__ = (
707 __table_args__ = (
706 UniqueConstraint('repository_id', 'field_key'), # no-multi field
708 UniqueConstraint('repository_id', 'field_key'), # no-multi field
707 {'extend_existing': True, 'mysql_engine': 'InnoDB',
709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
708 'mysql_charset': 'utf8'},
710 'mysql_charset': 'utf8'},
709 )
711 )
710 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
712 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
711
713
712 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
714 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
713 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
714 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
716 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
715 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
716 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
719 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_type = Column("field_type", String(256), nullable=False, unique=None)
720 field_type = Column("field_type", String(256), nullable=False, unique=None)
719 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
721 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
720
722
721 repository = relationship('Repository')
723 repository = relationship('Repository')
722
724
723 @property
725 @property
724 def field_key_prefixed(self):
726 def field_key_prefixed(self):
725 return 'ex_%s' % self.field_key
727 return 'ex_%s' % self.field_key
726
728
727 @classmethod
729 @classmethod
728 def un_prefix_key(cls, key):
730 def un_prefix_key(cls, key):
729 if key.startswith(cls.PREFIX):
731 if key.startswith(cls.PREFIX):
730 return key[len(cls.PREFIX):]
732 return key[len(cls.PREFIX):]
731 return key
733 return key
732
734
733 @classmethod
735 @classmethod
734 def get_by_key_name(cls, key, repo):
736 def get_by_key_name(cls, key, repo):
735 row = cls.query()\
737 row = cls.query()\
736 .filter(cls.repository == repo)\
738 .filter(cls.repository == repo)\
737 .filter(cls.field_key == key).scalar()
739 .filter(cls.field_key == key).scalar()
738 return row
740 return row
739
741
740
742
741 class Repository(Base, BaseModel):
743 class Repository(Base, BaseModel):
742 __tablename__ = 'repositories'
744 __tablename__ = 'repositories'
743 __table_args__ = (
745 __table_args__ = (
744 UniqueConstraint('repo_name'),
746 UniqueConstraint('repo_name'),
745 Index('r_repo_name_idx', 'repo_name'),
747 Index('r_repo_name_idx', 'repo_name'),
746 {'extend_existing': True, 'mysql_engine': 'InnoDB',
748 {'extend_existing': True, 'mysql_engine': 'InnoDB',
747 'mysql_charset': 'utf8'},
749 'mysql_charset': 'utf8'},
748 )
750 )
749
751
750 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
752 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
751 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
753 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
752 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
754 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
753 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
755 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
754 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
755 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
757 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
756 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
758 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
757 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
759 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
758 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
760 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
759 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
760 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
763 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
762 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
764 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
763 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
765 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
764 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
766 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
765
767
766 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
768 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
767 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
769 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
768
770
769 user = relationship('User')
771 user = relationship('User')
770 fork = relationship('Repository', remote_side=repo_id)
772 fork = relationship('Repository', remote_side=repo_id)
771 group = relationship('RepoGroup')
773 group = relationship('RepoGroup')
772 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
774 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
773 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
775 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
774 stats = relationship('Statistics', cascade='all', uselist=False)
776 stats = relationship('Statistics', cascade='all', uselist=False)
775
777
776 followers = relationship('UserFollowing',
778 followers = relationship('UserFollowing',
777 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
779 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
778 cascade='all')
780 cascade='all')
779 extra_fields = relationship('RepositoryField',
781 extra_fields = relationship('RepositoryField',
780 cascade="all, delete, delete-orphan")
782 cascade="all, delete, delete-orphan")
781
783
782 logs = relationship('UserLog')
784 logs = relationship('UserLog')
783 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
785 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
784
786
785 pull_requests_org = relationship('PullRequest',
787 pull_requests_org = relationship('PullRequest',
786 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
788 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
787 cascade="all, delete, delete-orphan")
789 cascade="all, delete, delete-orphan")
788
790
789 pull_requests_other = relationship('PullRequest',
791 pull_requests_other = relationship('PullRequest',
790 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
792 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
791 cascade="all, delete, delete-orphan")
793 cascade="all, delete, delete-orphan")
792
794
793 def __unicode__(self):
795 def __unicode__(self):
794 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
795 self.repo_name)
797 self.repo_name)
796
798
797 @hybrid_property
799 @hybrid_property
798 def locked(self):
800 def locked(self):
799 # always should return [user_id, timelocked]
801 # always should return [user_id, timelocked]
800 if self._locked:
802 if self._locked:
801 _lock_info = self._locked.split(':')
803 _lock_info = self._locked.split(':')
802 return int(_lock_info[0]), _lock_info[1]
804 return int(_lock_info[0]), _lock_info[1]
803 return [None, None]
805 return [None, None]
804
806
805 @locked.setter
807 @locked.setter
806 def locked(self, val):
808 def locked(self, val):
807 if val and isinstance(val, (list, tuple)):
809 if val and isinstance(val, (list, tuple)):
808 self._locked = ':'.join(map(str, val))
810 self._locked = ':'.join(map(str, val))
809 else:
811 else:
810 self._locked = None
812 self._locked = None
811
813
812 @hybrid_property
814 @hybrid_property
813 def changeset_cache(self):
815 def changeset_cache(self):
814 from rhodecode.lib.vcs.backends.base import EmptyChangeset
816 from rhodecode.lib.vcs.backends.base import EmptyChangeset
815 dummy = EmptyChangeset().__json__()
817 dummy = EmptyChangeset().__json__()
816 if not self._changeset_cache:
818 if not self._changeset_cache:
817 return dummy
819 return dummy
818 try:
820 try:
819 return json.loads(self._changeset_cache)
821 return json.loads(self._changeset_cache)
820 except TypeError:
822 except TypeError:
821 return dummy
823 return dummy
822
824
823 @changeset_cache.setter
825 @changeset_cache.setter
824 def changeset_cache(self, val):
826 def changeset_cache(self, val):
825 try:
827 try:
826 self._changeset_cache = json.dumps(val)
828 self._changeset_cache = json.dumps(val)
827 except Exception:
829 except Exception:
828 log.error(traceback.format_exc())
830 log.error(traceback.format_exc())
829
831
830 @classmethod
832 @classmethod
831 def url_sep(cls):
833 def url_sep(cls):
832 return URL_SEP
834 return URL_SEP
833
835
834 @classmethod
836 @classmethod
835 def normalize_repo_name(cls, repo_name):
837 def normalize_repo_name(cls, repo_name):
836 """
838 """
837 Normalizes os specific repo_name to the format internally stored inside
839 Normalizes os specific repo_name to the format internally stored inside
838 dabatabase using URL_SEP
840 dabatabase using URL_SEP
839
841
840 :param cls:
842 :param cls:
841 :param repo_name:
843 :param repo_name:
842 """
844 """
843 return cls.url_sep().join(repo_name.split(os.sep))
845 return cls.url_sep().join(repo_name.split(os.sep))
844
846
845 @classmethod
847 @classmethod
846 def get_by_repo_name(cls, repo_name):
848 def get_by_repo_name(cls, repo_name):
847 q = Session().query(cls).filter(cls.repo_name == repo_name)
849 q = Session().query(cls).filter(cls.repo_name == repo_name)
848 q = q.options(joinedload(Repository.fork))\
850 q = q.options(joinedload(Repository.fork))\
849 .options(joinedload(Repository.user))\
851 .options(joinedload(Repository.user))\
850 .options(joinedload(Repository.group))
852 .options(joinedload(Repository.group))
851 return q.scalar()
853 return q.scalar()
852
854
853 @classmethod
855 @classmethod
854 def get_by_full_path(cls, repo_full_path):
856 def get_by_full_path(cls, repo_full_path):
855 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
857 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
856 repo_name = cls.normalize_repo_name(repo_name)
858 repo_name = cls.normalize_repo_name(repo_name)
857 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
859 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
858
860
859 @classmethod
861 @classmethod
860 def get_repo_forks(cls, repo_id):
862 def get_repo_forks(cls, repo_id):
861 return cls.query().filter(Repository.fork_id == repo_id)
863 return cls.query().filter(Repository.fork_id == repo_id)
862
864
863 @classmethod
865 @classmethod
864 def base_path(cls):
866 def base_path(cls):
865 """
867 """
866 Returns base path when all repos are stored
868 Returns base path when all repos are stored
867
869
868 :param cls:
870 :param cls:
869 """
871 """
870 q = Session().query(RhodeCodeUi)\
872 q = Session().query(RhodeCodeUi)\
871 .filter(RhodeCodeUi.ui_key == cls.url_sep())
873 .filter(RhodeCodeUi.ui_key == cls.url_sep())
872 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
874 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
873 return q.one().ui_value
875 return q.one().ui_value
874
876
875 @property
877 @property
876 def forks(self):
878 def forks(self):
877 """
879 """
878 Return forks of this repo
880 Return forks of this repo
879 """
881 """
880 return Repository.get_repo_forks(self.repo_id)
882 return Repository.get_repo_forks(self.repo_id)
881
883
882 @property
884 @property
883 def parent(self):
885 def parent(self):
884 """
886 """
885 Returns fork parent
887 Returns fork parent
886 """
888 """
887 return self.fork
889 return self.fork
888
890
889 @property
891 @property
890 def just_name(self):
892 def just_name(self):
891 return self.repo_name.split(Repository.url_sep())[-1]
893 return self.repo_name.split(Repository.url_sep())[-1]
892
894
893 @property
895 @property
894 def groups_with_parents(self):
896 def groups_with_parents(self):
895 groups = []
897 groups = []
896 if self.group is None:
898 if self.group is None:
897 return groups
899 return groups
898
900
899 cur_gr = self.group
901 cur_gr = self.group
900 groups.insert(0, cur_gr)
902 groups.insert(0, cur_gr)
901 while 1:
903 while 1:
902 gr = getattr(cur_gr, 'parent_group', None)
904 gr = getattr(cur_gr, 'parent_group', None)
903 cur_gr = cur_gr.parent_group
905 cur_gr = cur_gr.parent_group
904 if gr is None:
906 if gr is None:
905 break
907 break
906 groups.insert(0, gr)
908 groups.insert(0, gr)
907
909
908 return groups
910 return groups
909
911
910 @property
912 @property
911 def groups_and_repo(self):
913 def groups_and_repo(self):
912 return self.groups_with_parents, self.just_name, self.repo_name
914 return self.groups_with_parents, self.just_name, self.repo_name
913
915
914 @LazyProperty
916 @LazyProperty
915 def repo_path(self):
917 def repo_path(self):
916 """
918 """
917 Returns base full path for that repository means where it actually
919 Returns base full path for that repository means where it actually
918 exists on a filesystem
920 exists on a filesystem
919 """
921 """
920 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
922 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
921 Repository.url_sep())
923 Repository.url_sep())
922 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
924 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
923 return q.one().ui_value
925 return q.one().ui_value
924
926
925 @property
927 @property
926 def repo_full_path(self):
928 def repo_full_path(self):
927 p = [self.repo_path]
929 p = [self.repo_path]
928 # we need to split the name by / since this is how we store the
930 # we need to split the name by / since this is how we store the
929 # names in the database, but that eventually needs to be converted
931 # names in the database, but that eventually needs to be converted
930 # into a valid system path
932 # into a valid system path
931 p += self.repo_name.split(Repository.url_sep())
933 p += self.repo_name.split(Repository.url_sep())
932 return os.path.join(*map(safe_unicode, p))
934 return os.path.join(*map(safe_unicode, p))
933
935
934 @property
936 @property
935 def cache_keys(self):
937 def cache_keys(self):
936 """
938 """
937 Returns associated cache keys for that repo
939 Returns associated cache keys for that repo
938 """
940 """
939 return CacheInvalidation.query()\
941 return CacheInvalidation.query()\
940 .filter(CacheInvalidation.cache_args == self.repo_name)\
942 .filter(CacheInvalidation.cache_args == self.repo_name)\
941 .order_by(CacheInvalidation.cache_key)\
943 .order_by(CacheInvalidation.cache_key)\
942 .all()
944 .all()
943
945
944 def get_new_name(self, repo_name):
946 def get_new_name(self, repo_name):
945 """
947 """
946 returns new full repository name based on assigned group and new new
948 returns new full repository name based on assigned group and new new
947
949
948 :param group_name:
950 :param group_name:
949 """
951 """
950 path_prefix = self.group.full_path_splitted if self.group else []
952 path_prefix = self.group.full_path_splitted if self.group else []
951 return Repository.url_sep().join(path_prefix + [repo_name])
953 return Repository.url_sep().join(path_prefix + [repo_name])
952
954
953 @property
955 @property
954 def _ui(self):
956 def _ui(self):
955 """
957 """
956 Creates an db based ui object for this repository
958 Creates an db based ui object for this repository
957 """
959 """
958 from rhodecode.lib.utils import make_ui
960 from rhodecode.lib.utils import make_ui
959 return make_ui('db', clear_session=False)
961 return make_ui('db', clear_session=False)
960
962
961 @classmethod
963 @classmethod
962 def is_valid(cls, repo_name):
964 def is_valid(cls, repo_name):
963 """
965 """
964 returns True if given repo name is a valid filesystem repository
966 returns True if given repo name is a valid filesystem repository
965
967
966 :param cls:
968 :param cls:
967 :param repo_name:
969 :param repo_name:
968 """
970 """
969 from rhodecode.lib.utils import is_valid_repo
971 from rhodecode.lib.utils import is_valid_repo
970
972
971 return is_valid_repo(repo_name, cls.base_path())
973 return is_valid_repo(repo_name, cls.base_path())
972
974
973 def get_api_data(self):
975 def get_api_data(self):
974 """
976 """
975 Common function for generating repo api data
977 Common function for generating repo api data
976
978
977 """
979 """
978 repo = self
980 repo = self
979 data = dict(
981 data = dict(
980 repo_id=repo.repo_id,
982 repo_id=repo.repo_id,
981 repo_name=repo.repo_name,
983 repo_name=repo.repo_name,
982 repo_type=repo.repo_type,
984 repo_type=repo.repo_type,
983 clone_uri=repo.clone_uri,
985 clone_uri=repo.clone_uri,
984 private=repo.private,
986 private=repo.private,
985 created_on=repo.created_on,
987 created_on=repo.created_on,
986 description=repo.description,
988 description=repo.description,
987 landing_rev=repo.landing_rev,
989 landing_rev=repo.landing_rev,
988 owner=repo.user.username,
990 owner=repo.user.username,
989 fork_of=repo.fork.repo_name if repo.fork else None,
991 fork_of=repo.fork.repo_name if repo.fork else None,
990 enable_statistics=repo.enable_statistics,
992 enable_statistics=repo.enable_statistics,
991 enable_locking=repo.enable_locking,
993 enable_locking=repo.enable_locking,
992 enable_downloads=repo.enable_downloads,
994 enable_downloads=repo.enable_downloads,
993 last_changeset=repo.changeset_cache,
995 last_changeset=repo.changeset_cache,
994 locked_by=User.get(self.locked[0]).get_api_data() \
996 locked_by=User.get(self.locked[0]).get_api_data() \
995 if self.locked[0] else None,
997 if self.locked[0] else None,
996 locked_date=time_to_datetime(self.locked[1]) \
998 locked_date=time_to_datetime(self.locked[1]) \
997 if self.locked[1] else None
999 if self.locked[1] else None
998 )
1000 )
999 rc_config = RhodeCodeSetting.get_app_settings()
1001 rc_config = RhodeCodeSetting.get_app_settings()
1000 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1002 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1001 if repository_fields:
1003 if repository_fields:
1002 for f in self.extra_fields:
1004 for f in self.extra_fields:
1003 data[f.field_key_prefixed] = f.field_value
1005 data[f.field_key_prefixed] = f.field_value
1004
1006
1005 return data
1007 return data
1006
1008
1007 @classmethod
1009 @classmethod
1008 def lock(cls, repo, user_id):
1010 def lock(cls, repo, user_id):
1009 repo.locked = [user_id, time.time()]
1011 repo.locked = [user_id, time.time()]
1010 Session().add(repo)
1012 Session().add(repo)
1011 Session().commit()
1013 Session().commit()
1012
1014
1013 @classmethod
1015 @classmethod
1014 def unlock(cls, repo):
1016 def unlock(cls, repo):
1015 repo.locked = None
1017 repo.locked = None
1016 Session().add(repo)
1018 Session().add(repo)
1017 Session().commit()
1019 Session().commit()
1018
1020
1019 @classmethod
1021 @classmethod
1020 def getlock(cls, repo):
1022 def getlock(cls, repo):
1021 return repo.locked
1023 return repo.locked
1022
1024
1023 @property
1025 @property
1024 def last_db_change(self):
1026 def last_db_change(self):
1025 return self.updated_on
1027 return self.updated_on
1026
1028
1027 def clone_url(self, **override):
1029 def clone_url(self, **override):
1028 from pylons import url
1030 from pylons import url
1029 from urlparse import urlparse
1031 from urlparse import urlparse
1030 import urllib
1032 import urllib
1031 parsed_url = urlparse(url('home', qualified=True))
1033 parsed_url = urlparse(url('home', qualified=True))
1032 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1034 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1033 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1035 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1034 args = {
1036 args = {
1035 'user': '',
1037 'user': '',
1036 'pass': '',
1038 'pass': '',
1037 'scheme': parsed_url.scheme,
1039 'scheme': parsed_url.scheme,
1038 'netloc': parsed_url.netloc,
1040 'netloc': parsed_url.netloc,
1039 'prefix': decoded_path,
1041 'prefix': decoded_path,
1040 'path': self.repo_name
1042 'path': self.repo_name
1041 }
1043 }
1042
1044
1043 args.update(override)
1045 args.update(override)
1044 return default_clone_uri % args
1046 return default_clone_uri % args
1045
1047
1046 #==========================================================================
1048 #==========================================================================
1047 # SCM PROPERTIES
1049 # SCM PROPERTIES
1048 #==========================================================================
1050 #==========================================================================
1049
1051
1050 def get_changeset(self, rev=None):
1052 def get_changeset(self, rev=None):
1051 return get_changeset_safe(self.scm_instance, rev)
1053 return get_changeset_safe(self.scm_instance, rev)
1052
1054
1053 def get_landing_changeset(self):
1055 def get_landing_changeset(self):
1054 """
1056 """
1055 Returns landing changeset, or if that doesn't exist returns the tip
1057 Returns landing changeset, or if that doesn't exist returns the tip
1056 """
1058 """
1057 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1059 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1058 return cs
1060 return cs
1059
1061
1060 def update_changeset_cache(self, cs_cache=None):
1062 def update_changeset_cache(self, cs_cache=None):
1061 """
1063 """
1062 Update cache of last changeset for repository, keys should be::
1064 Update cache of last changeset for repository, keys should be::
1063
1065
1064 short_id
1066 short_id
1065 raw_id
1067 raw_id
1066 revision
1068 revision
1067 message
1069 message
1068 date
1070 date
1069 author
1071 author
1070
1072
1071 :param cs_cache:
1073 :param cs_cache:
1072 """
1074 """
1073 from rhodecode.lib.vcs.backends.base import BaseChangeset
1075 from rhodecode.lib.vcs.backends.base import BaseChangeset
1074 if cs_cache is None:
1076 if cs_cache is None:
1075 cs_cache = EmptyChangeset()
1077 cs_cache = EmptyChangeset()
1076 # use no-cache version here
1078 # use no-cache version here
1077 scm_repo = self.scm_instance_no_cache()
1079 scm_repo = self.scm_instance_no_cache()
1078 if scm_repo:
1080 if scm_repo:
1079 cs_cache = scm_repo.get_changeset()
1081 cs_cache = scm_repo.get_changeset()
1080
1082
1081 if isinstance(cs_cache, BaseChangeset):
1083 if isinstance(cs_cache, BaseChangeset):
1082 cs_cache = cs_cache.__json__()
1084 cs_cache = cs_cache.__json__()
1083
1085
1084 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1086 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1085 _default = datetime.datetime.fromtimestamp(0)
1087 _default = datetime.datetime.fromtimestamp(0)
1086 last_change = cs_cache.get('date') or _default
1088 last_change = cs_cache.get('date') or _default
1087 log.debug('updated repo %s with new cs cache %s'
1089 log.debug('updated repo %s with new cs cache %s'
1088 % (self.repo_name, cs_cache))
1090 % (self.repo_name, cs_cache))
1089 self.updated_on = last_change
1091 self.updated_on = last_change
1090 self.changeset_cache = cs_cache
1092 self.changeset_cache = cs_cache
1091 Session().add(self)
1093 Session().add(self)
1092 Session().commit()
1094 Session().commit()
1093 else:
1095 else:
1094 log.debug('Skipping repo:%s already with latest changes'
1096 log.debug('Skipping repo:%s already with latest changes'
1095 % self.repo_name)
1097 % self.repo_name)
1096
1098
1097 @property
1099 @property
1098 def tip(self):
1100 def tip(self):
1099 return self.get_changeset('tip')
1101 return self.get_changeset('tip')
1100
1102
1101 @property
1103 @property
1102 def author(self):
1104 def author(self):
1103 return self.tip.author
1105 return self.tip.author
1104
1106
1105 @property
1107 @property
1106 def last_change(self):
1108 def last_change(self):
1107 return self.scm_instance.last_change
1109 return self.scm_instance.last_change
1108
1110
1109 def get_comments(self, revisions=None):
1111 def get_comments(self, revisions=None):
1110 """
1112 """
1111 Returns comments for this repository grouped by revisions
1113 Returns comments for this repository grouped by revisions
1112
1114
1113 :param revisions: filter query by revisions only
1115 :param revisions: filter query by revisions only
1114 """
1116 """
1115 cmts = ChangesetComment.query()\
1117 cmts = ChangesetComment.query()\
1116 .filter(ChangesetComment.repo == self)
1118 .filter(ChangesetComment.repo == self)
1117 if revisions:
1119 if revisions:
1118 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1120 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1119 grouped = defaultdict(list)
1121 grouped = defaultdict(list)
1120 for cmt in cmts.all():
1122 for cmt in cmts.all():
1121 grouped[cmt.revision].append(cmt)
1123 grouped[cmt.revision].append(cmt)
1122 return grouped
1124 return grouped
1123
1125
1124 def statuses(self, revisions=None):
1126 def statuses(self, revisions=None):
1125 """
1127 """
1126 Returns statuses for this repository
1128 Returns statuses for this repository
1127
1129
1128 :param revisions: list of revisions to get statuses for
1130 :param revisions: list of revisions to get statuses for
1129 :type revisions: list
1131 :type revisions: list
1130 """
1132 """
1131
1133
1132 statuses = ChangesetStatus.query()\
1134 statuses = ChangesetStatus.query()\
1133 .filter(ChangesetStatus.repo == self)\
1135 .filter(ChangesetStatus.repo == self)\
1134 .filter(ChangesetStatus.version == 0)
1136 .filter(ChangesetStatus.version == 0)
1135 if revisions:
1137 if revisions:
1136 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1138 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1137 grouped = {}
1139 grouped = {}
1138
1140
1139 #maybe we have open new pullrequest without a status ?
1141 #maybe we have open new pullrequest without a status ?
1140 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1142 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1141 status_lbl = ChangesetStatus.get_status_lbl(stat)
1143 status_lbl = ChangesetStatus.get_status_lbl(stat)
1142 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1144 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1143 for rev in pr.revisions:
1145 for rev in pr.revisions:
1144 pr_id = pr.pull_request_id
1146 pr_id = pr.pull_request_id
1145 pr_repo = pr.other_repo.repo_name
1147 pr_repo = pr.other_repo.repo_name
1146 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1148 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1147
1149
1148 for stat in statuses.all():
1150 for stat in statuses.all():
1149 pr_id = pr_repo = None
1151 pr_id = pr_repo = None
1150 if stat.pull_request:
1152 if stat.pull_request:
1151 pr_id = stat.pull_request.pull_request_id
1153 pr_id = stat.pull_request.pull_request_id
1152 pr_repo = stat.pull_request.other_repo.repo_name
1154 pr_repo = stat.pull_request.other_repo.repo_name
1153 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1155 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1154 pr_id, pr_repo]
1156 pr_id, pr_repo]
1155 return grouped
1157 return grouped
1156
1158
1157 def _repo_size(self):
1159 def _repo_size(self):
1158 from rhodecode.lib import helpers as h
1160 from rhodecode.lib import helpers as h
1159 log.debug('calculating repository size...')
1161 log.debug('calculating repository size...')
1160 return h.format_byte_size(self.scm_instance.size)
1162 return h.format_byte_size(self.scm_instance.size)
1161
1163
1162 #==========================================================================
1164 #==========================================================================
1163 # SCM CACHE INSTANCE
1165 # SCM CACHE INSTANCE
1164 #==========================================================================
1166 #==========================================================================
1165
1167
1166 def set_invalidate(self):
1168 def set_invalidate(self):
1167 """
1169 """
1168 Mark caches of this repo as invalid.
1170 Mark caches of this repo as invalid.
1169 """
1171 """
1170 CacheInvalidation.set_invalidate(self.repo_name)
1172 CacheInvalidation.set_invalidate(self.repo_name)
1171
1173
1172 def scm_instance_no_cache(self):
1174 def scm_instance_no_cache(self):
1173 return self.__get_instance()
1175 return self.__get_instance()
1174
1176
1175 @property
1177 @property
1176 def scm_instance(self):
1178 def scm_instance(self):
1177 import rhodecode
1179 import rhodecode
1178 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1180 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1179 if full_cache:
1181 if full_cache:
1180 return self.scm_instance_cached()
1182 return self.scm_instance_cached()
1181 return self.__get_instance()
1183 return self.__get_instance()
1182
1184
1183 def scm_instance_cached(self, valid_cache_keys=None):
1185 def scm_instance_cached(self, valid_cache_keys=None):
1184 @cache_region('long_term')
1186 @cache_region('long_term')
1185 def _c(repo_name):
1187 def _c(repo_name):
1186 return self.__get_instance()
1188 return self.__get_instance()
1187 rn = self.repo_name
1189 rn = self.repo_name
1188
1190
1189 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1191 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1190 if not valid:
1192 if not valid:
1191 log.debug('Cache for %s invalidated, getting new object' % (rn))
1193 log.debug('Cache for %s invalidated, getting new object' % (rn))
1192 region_invalidate(_c, None, rn)
1194 region_invalidate(_c, None, rn)
1193 else:
1195 else:
1194 log.debug('Getting obj for %s from cache' % (rn))
1196 log.debug('Getting obj for %s from cache' % (rn))
1195 return _c(rn)
1197 return _c(rn)
1196
1198
1197 def __get_instance(self):
1199 def __get_instance(self):
1198 repo_full_path = self.repo_full_path
1200 repo_full_path = self.repo_full_path
1199 try:
1201 try:
1200 alias = get_scm(repo_full_path)[0]
1202 alias = get_scm(repo_full_path)[0]
1201 log.debug('Creating instance of %s repository from %s'
1203 log.debug('Creating instance of %s repository from %s'
1202 % (alias, repo_full_path))
1204 % (alias, repo_full_path))
1203 backend = get_backend(alias)
1205 backend = get_backend(alias)
1204 except VCSError:
1206 except VCSError:
1205 log.error(traceback.format_exc())
1207 log.error(traceback.format_exc())
1206 log.error('Perhaps this repository is in db and not in '
1208 log.error('Perhaps this repository is in db and not in '
1207 'filesystem run rescan repositories with '
1209 'filesystem run rescan repositories with '
1208 '"destroy old data " option from admin panel')
1210 '"destroy old data " option from admin panel')
1209 return
1211 return
1210
1212
1211 if alias == 'hg':
1213 if alias == 'hg':
1212
1214
1213 repo = backend(safe_str(repo_full_path), create=False,
1215 repo = backend(safe_str(repo_full_path), create=False,
1214 baseui=self._ui)
1216 baseui=self._ui)
1215 # skip hidden web repository
1217 # skip hidden web repository
1216 if repo._get_hidden():
1218 if repo._get_hidden():
1217 return
1219 return
1218 else:
1220 else:
1219 repo = backend(repo_full_path, create=False)
1221 repo = backend(repo_full_path, create=False)
1220
1222
1221 return repo
1223 return repo
1222
1224
1223
1225
1224 class RepoGroup(Base, BaseModel):
1226 class RepoGroup(Base, BaseModel):
1225 __tablename__ = 'groups'
1227 __tablename__ = 'groups'
1226 __table_args__ = (
1228 __table_args__ = (
1227 UniqueConstraint('group_name', 'group_parent_id'),
1229 UniqueConstraint('group_name', 'group_parent_id'),
1228 CheckConstraint('group_id != group_parent_id'),
1230 CheckConstraint('group_id != group_parent_id'),
1229 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1231 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1230 'mysql_charset': 'utf8'},
1232 'mysql_charset': 'utf8'},
1231 )
1233 )
1232 __mapper_args__ = {'order_by': 'group_name'}
1234 __mapper_args__ = {'order_by': 'group_name'}
1233
1235
1234 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1236 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1235 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1237 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1236 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1238 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1237 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1239 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1238 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1240 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1239 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1241 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1240
1242
1241 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1243 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1242 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1244 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1243 parent_group = relationship('RepoGroup', remote_side=group_id)
1245 parent_group = relationship('RepoGroup', remote_side=group_id)
1244 user = relationship('User')
1246 user = relationship('User')
1245
1247
1246 def __init__(self, group_name='', parent_group=None):
1248 def __init__(self, group_name='', parent_group=None):
1247 self.group_name = group_name
1249 self.group_name = group_name
1248 self.parent_group = parent_group
1250 self.parent_group = parent_group
1249
1251
1250 def __unicode__(self):
1252 def __unicode__(self):
1251 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1253 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1252 self.group_name)
1254 self.group_name)
1253
1255
1254 @classmethod
1256 @classmethod
1255 def groups_choices(cls, groups=None, show_empty_group=True):
1257 def groups_choices(cls, groups=None, show_empty_group=True):
1256 from webhelpers.html import literal as _literal
1258 from webhelpers.html import literal as _literal
1257 if not groups:
1259 if not groups:
1258 groups = cls.query().all()
1260 groups = cls.query().all()
1259
1261
1260 repo_groups = []
1262 repo_groups = []
1261 if show_empty_group:
1263 if show_empty_group:
1262 repo_groups = [('-1', '-- %s --' % _('top level'))]
1264 repo_groups = [('-1', '-- %s --' % _('top level'))]
1263 sep = ' &raquo; '
1265 sep = ' &raquo; '
1264 _name = lambda k: _literal(sep.join(k))
1266 _name = lambda k: _literal(sep.join(k))
1265
1267
1266 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1268 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1267 for x in groups])
1269 for x in groups])
1268
1270
1269 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1271 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1270 return repo_groups
1272 return repo_groups
1271
1273
1272 @classmethod
1274 @classmethod
1273 def url_sep(cls):
1275 def url_sep(cls):
1274 return URL_SEP
1276 return URL_SEP
1275
1277
1276 @classmethod
1278 @classmethod
1277 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1279 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1278 if case_insensitive:
1280 if case_insensitive:
1279 gr = cls.query()\
1281 gr = cls.query()\
1280 .filter(cls.group_name.ilike(group_name))
1282 .filter(cls.group_name.ilike(group_name))
1281 else:
1283 else:
1282 gr = cls.query()\
1284 gr = cls.query()\
1283 .filter(cls.group_name == group_name)
1285 .filter(cls.group_name == group_name)
1284 if cache:
1286 if cache:
1285 gr = gr.options(FromCache(
1287 gr = gr.options(FromCache(
1286 "sql_cache_short",
1288 "sql_cache_short",
1287 "get_group_%s" % _hash_key(group_name)
1289 "get_group_%s" % _hash_key(group_name)
1288 )
1290 )
1289 )
1291 )
1290 return gr.scalar()
1292 return gr.scalar()
1291
1293
1292 @property
1294 @property
1293 def parents(self):
1295 def parents(self):
1294 parents_recursion_limit = 5
1296 parents_recursion_limit = 5
1295 groups = []
1297 groups = []
1296 if self.parent_group is None:
1298 if self.parent_group is None:
1297 return groups
1299 return groups
1298 cur_gr = self.parent_group
1300 cur_gr = self.parent_group
1299 groups.insert(0, cur_gr)
1301 groups.insert(0, cur_gr)
1300 cnt = 0
1302 cnt = 0
1301 while 1:
1303 while 1:
1302 cnt += 1
1304 cnt += 1
1303 gr = getattr(cur_gr, 'parent_group', None)
1305 gr = getattr(cur_gr, 'parent_group', None)
1304 cur_gr = cur_gr.parent_group
1306 cur_gr = cur_gr.parent_group
1305 if gr is None:
1307 if gr is None:
1306 break
1308 break
1307 if cnt == parents_recursion_limit:
1309 if cnt == parents_recursion_limit:
1308 # this will prevent accidental infinit loops
1310 # this will prevent accidental infinit loops
1309 log.error('group nested more than %s' %
1311 log.error('group nested more than %s' %
1310 parents_recursion_limit)
1312 parents_recursion_limit)
1311 break
1313 break
1312
1314
1313 groups.insert(0, gr)
1315 groups.insert(0, gr)
1314 return groups
1316 return groups
1315
1317
1316 @property
1318 @property
1317 def children(self):
1319 def children(self):
1318 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1320 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1319
1321
1320 @property
1322 @property
1321 def name(self):
1323 def name(self):
1322 return self.group_name.split(RepoGroup.url_sep())[-1]
1324 return self.group_name.split(RepoGroup.url_sep())[-1]
1323
1325
1324 @property
1326 @property
1325 def full_path(self):
1327 def full_path(self):
1326 return self.group_name
1328 return self.group_name
1327
1329
1328 @property
1330 @property
1329 def full_path_splitted(self):
1331 def full_path_splitted(self):
1330 return self.group_name.split(RepoGroup.url_sep())
1332 return self.group_name.split(RepoGroup.url_sep())
1331
1333
1332 @property
1334 @property
1333 def repositories(self):
1335 def repositories(self):
1334 return Repository.query()\
1336 return Repository.query()\
1335 .filter(Repository.group == self)\
1337 .filter(Repository.group == self)\
1336 .order_by(Repository.repo_name)
1338 .order_by(Repository.repo_name)
1337
1339
1338 @property
1340 @property
1339 def repositories_recursive_count(self):
1341 def repositories_recursive_count(self):
1340 cnt = self.repositories.count()
1342 cnt = self.repositories.count()
1341
1343
1342 def children_count(group):
1344 def children_count(group):
1343 cnt = 0
1345 cnt = 0
1344 for child in group.children:
1346 for child in group.children:
1345 cnt += child.repositories.count()
1347 cnt += child.repositories.count()
1346 cnt += children_count(child)
1348 cnt += children_count(child)
1347 return cnt
1349 return cnt
1348
1350
1349 return cnt + children_count(self)
1351 return cnt + children_count(self)
1350
1352
1351 def _recursive_objects(self, include_repos=True):
1353 def _recursive_objects(self, include_repos=True):
1352 all_ = []
1354 all_ = []
1353
1355
1354 def _get_members(root_gr):
1356 def _get_members(root_gr):
1355 if include_repos:
1357 if include_repos:
1356 for r in root_gr.repositories:
1358 for r in root_gr.repositories:
1357 all_.append(r)
1359 all_.append(r)
1358 childs = root_gr.children.all()
1360 childs = root_gr.children.all()
1359 if childs:
1361 if childs:
1360 for gr in childs:
1362 for gr in childs:
1361 all_.append(gr)
1363 all_.append(gr)
1362 _get_members(gr)
1364 _get_members(gr)
1363
1365
1364 _get_members(self)
1366 _get_members(self)
1365 return [self] + all_
1367 return [self] + all_
1366
1368
1367 def recursive_groups_and_repos(self):
1369 def recursive_groups_and_repos(self):
1368 """
1370 """
1369 Recursive return all groups, with repositories in those groups
1371 Recursive return all groups, with repositories in those groups
1370 """
1372 """
1371 return self._recursive_objects()
1373 return self._recursive_objects()
1372
1374
1373 def recursive_groups(self):
1375 def recursive_groups(self):
1374 """
1376 """
1375 Returns all children groups for this group including children of children
1377 Returns all children groups for this group including children of children
1376 """
1378 """
1377 return self._recursive_objects(include_repos=False)
1379 return self._recursive_objects(include_repos=False)
1378
1380
1379 def get_new_name(self, group_name):
1381 def get_new_name(self, group_name):
1380 """
1382 """
1381 returns new full group name based on parent and new name
1383 returns new full group name based on parent and new name
1382
1384
1383 :param group_name:
1385 :param group_name:
1384 """
1386 """
1385 path_prefix = (self.parent_group.full_path_splitted if
1387 path_prefix = (self.parent_group.full_path_splitted if
1386 self.parent_group else [])
1388 self.parent_group else [])
1387 return RepoGroup.url_sep().join(path_prefix + [group_name])
1389 return RepoGroup.url_sep().join(path_prefix + [group_name])
1388
1390
1389
1391
1390 class Permission(Base, BaseModel):
1392 class Permission(Base, BaseModel):
1391 __tablename__ = 'permissions'
1393 __tablename__ = 'permissions'
1392 __table_args__ = (
1394 __table_args__ = (
1393 Index('p_perm_name_idx', 'permission_name'),
1395 Index('p_perm_name_idx', 'permission_name'),
1394 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1396 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1395 'mysql_charset': 'utf8'},
1397 'mysql_charset': 'utf8'},
1396 )
1398 )
1397 PERMS = [
1399 PERMS = [
1398 ('hg.admin', _('RhodeCode Administrator')),
1400 ('hg.admin', _('RhodeCode Administrator')),
1399
1401
1400 ('repository.none', _('Repository no access')),
1402 ('repository.none', _('Repository no access')),
1401 ('repository.read', _('Repository read access')),
1403 ('repository.read', _('Repository read access')),
1402 ('repository.write', _('Repository write access')),
1404 ('repository.write', _('Repository write access')),
1403 ('repository.admin', _('Repository admin access')),
1405 ('repository.admin', _('Repository admin access')),
1404
1406
1405 ('group.none', _('Repository group no access')),
1407 ('group.none', _('Repository group no access')),
1406 ('group.read', _('Repository group read access')),
1408 ('group.read', _('Repository group read access')),
1407 ('group.write', _('Repository group write access')),
1409 ('group.write', _('Repository group write access')),
1408 ('group.admin', _('Repository group admin access')),
1410 ('group.admin', _('Repository group admin access')),
1409
1411
1410 ('usergroup.none', _('User group no access')),
1412 ('usergroup.none', _('User group no access')),
1411 ('usergroup.read', _('User group read access')),
1413 ('usergroup.read', _('User group read access')),
1412 ('usergroup.write', _('User group write access')),
1414 ('usergroup.write', _('User group write access')),
1413 ('usergroup.admin', _('User group admin access')),
1415 ('usergroup.admin', _('User group admin access')),
1414
1416
1415 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1417 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1416 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1418 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1417
1419
1418 ('hg.usergroup.create.false', _('User Group creation disabled')),
1420 ('hg.usergroup.create.false', _('User Group creation disabled')),
1419 ('hg.usergroup.create.true', _('User Group creation enabled')),
1421 ('hg.usergroup.create.true', _('User Group creation enabled')),
1420
1422
1421 ('hg.create.none', _('Repository creation disabled')),
1423 ('hg.create.none', _('Repository creation disabled')),
1422 ('hg.create.repository', _('Repository creation enabled')),
1424 ('hg.create.repository', _('Repository creation enabled')),
1423
1425
1424 ('hg.fork.none', _('Repository forking disabled')),
1426 ('hg.fork.none', _('Repository forking disabled')),
1425 ('hg.fork.repository', _('Repository forking enabled')),
1427 ('hg.fork.repository', _('Repository forking enabled')),
1426
1428
1427 ('hg.register.none', _('Registration disabled')),
1429 ('hg.register.none', _('Registration disabled')),
1428 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1430 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1429 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1431 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1430
1432
1431 ('hg.extern_activate.manual', _('Manual activation of external account')),
1433 ('hg.extern_activate.manual', _('Manual activation of external account')),
1432 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1434 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1433
1435
1434 ]
1436 ]
1435
1437
1436 #definition of system default permissions for DEFAULT user
1438 #definition of system default permissions for DEFAULT user
1437 DEFAULT_USER_PERMISSIONS = [
1439 DEFAULT_USER_PERMISSIONS = [
1438 'repository.read',
1440 'repository.read',
1439 'group.read',
1441 'group.read',
1440 'usergroup.read',
1442 'usergroup.read',
1441 'hg.create.repository',
1443 'hg.create.repository',
1442 'hg.fork.repository',
1444 'hg.fork.repository',
1443 'hg.register.manual_activate',
1445 'hg.register.manual_activate',
1444 'hg.extern_activate.auto',
1446 'hg.extern_activate.auto',
1445 ]
1447 ]
1446
1448
1447 # defines which permissions are more important higher the more important
1449 # defines which permissions are more important higher the more important
1448 # Weight defines which permissions are more important.
1450 # Weight defines which permissions are more important.
1449 # The higher number the more important.
1451 # The higher number the more important.
1450 PERM_WEIGHTS = {
1452 PERM_WEIGHTS = {
1451 'repository.none': 0,
1453 'repository.none': 0,
1452 'repository.read': 1,
1454 'repository.read': 1,
1453 'repository.write': 3,
1455 'repository.write': 3,
1454 'repository.admin': 4,
1456 'repository.admin': 4,
1455
1457
1456 'group.none': 0,
1458 'group.none': 0,
1457 'group.read': 1,
1459 'group.read': 1,
1458 'group.write': 3,
1460 'group.write': 3,
1459 'group.admin': 4,
1461 'group.admin': 4,
1460
1462
1461 'usergroup.none': 0,
1463 'usergroup.none': 0,
1462 'usergroup.read': 1,
1464 'usergroup.read': 1,
1463 'usergroup.write': 3,
1465 'usergroup.write': 3,
1464 'usergroup.admin': 4,
1466 'usergroup.admin': 4,
1465 'hg.repogroup.create.false': 0,
1467 'hg.repogroup.create.false': 0,
1466 'hg.repogroup.create.true': 1,
1468 'hg.repogroup.create.true': 1,
1467
1469
1468 'hg.usergroup.create.false': 0,
1470 'hg.usergroup.create.false': 0,
1469 'hg.usergroup.create.true': 1,
1471 'hg.usergroup.create.true': 1,
1470
1472
1471 'hg.fork.none': 0,
1473 'hg.fork.none': 0,
1472 'hg.fork.repository': 1,
1474 'hg.fork.repository': 1,
1473 'hg.create.none': 0,
1475 'hg.create.none': 0,
1474 'hg.create.repository': 1
1476 'hg.create.repository': 1
1475 }
1477 }
1476
1478
1477 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1479 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1478 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1480 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1479 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1481 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1480
1482
1481 def __unicode__(self):
1483 def __unicode__(self):
1482 return u"<%s('%s:%s')>" % (
1484 return u"<%s('%s:%s')>" % (
1483 self.__class__.__name__, self.permission_id, self.permission_name
1485 self.__class__.__name__, self.permission_id, self.permission_name
1484 )
1486 )
1485
1487
1486 @classmethod
1488 @classmethod
1487 def get_by_key(cls, key):
1489 def get_by_key(cls, key):
1488 return cls.query().filter(cls.permission_name == key).scalar()
1490 return cls.query().filter(cls.permission_name == key).scalar()
1489
1491
1490 @classmethod
1492 @classmethod
1491 def get_default_perms(cls, default_user_id):
1493 def get_default_perms(cls, default_user_id):
1492 q = Session().query(UserRepoToPerm, Repository, cls)\
1494 q = Session().query(UserRepoToPerm, Repository, cls)\
1493 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1495 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1494 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1496 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1495 .filter(UserRepoToPerm.user_id == default_user_id)
1497 .filter(UserRepoToPerm.user_id == default_user_id)
1496
1498
1497 return q.all()
1499 return q.all()
1498
1500
1499 @classmethod
1501 @classmethod
1500 def get_default_group_perms(cls, default_user_id):
1502 def get_default_group_perms(cls, default_user_id):
1501 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1503 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1502 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1504 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1503 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1505 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1504 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1506 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1505
1507
1506 return q.all()
1508 return q.all()
1507
1509
1508 @classmethod
1510 @classmethod
1509 def get_default_user_group_perms(cls, default_user_id):
1511 def get_default_user_group_perms(cls, default_user_id):
1510 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1512 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1511 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1513 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1512 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1514 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1513 .filter(UserUserGroupToPerm.user_id == default_user_id)
1515 .filter(UserUserGroupToPerm.user_id == default_user_id)
1514
1516
1515 return q.all()
1517 return q.all()
1516
1518
1517
1519
1518 class UserRepoToPerm(Base, BaseModel):
1520 class UserRepoToPerm(Base, BaseModel):
1519 __tablename__ = 'repo_to_perm'
1521 __tablename__ = 'repo_to_perm'
1520 __table_args__ = (
1522 __table_args__ = (
1521 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1523 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1522 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1524 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1523 'mysql_charset': 'utf8'}
1525 'mysql_charset': 'utf8'}
1524 )
1526 )
1525 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1527 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1526 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1528 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1527 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1529 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1528 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1530 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1529
1531
1530 user = relationship('User')
1532 user = relationship('User')
1531 repository = relationship('Repository')
1533 repository = relationship('Repository')
1532 permission = relationship('Permission')
1534 permission = relationship('Permission')
1533
1535
1534 @classmethod
1536 @classmethod
1535 def create(cls, user, repository, permission):
1537 def create(cls, user, repository, permission):
1536 n = cls()
1538 n = cls()
1537 n.user = user
1539 n.user = user
1538 n.repository = repository
1540 n.repository = repository
1539 n.permission = permission
1541 n.permission = permission
1540 Session().add(n)
1542 Session().add(n)
1541 return n
1543 return n
1542
1544
1543 def __unicode__(self):
1545 def __unicode__(self):
1544 return u'<%s => %s >' % (self.user, self.repository)
1546 return u'<%s => %s >' % (self.user, self.repository)
1545
1547
1546
1548
1547 class UserUserGroupToPerm(Base, BaseModel):
1549 class UserUserGroupToPerm(Base, BaseModel):
1548 __tablename__ = 'user_user_group_to_perm'
1550 __tablename__ = 'user_user_group_to_perm'
1549 __table_args__ = (
1551 __table_args__ = (
1550 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1552 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1551 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1552 'mysql_charset': 'utf8'}
1554 'mysql_charset': 'utf8'}
1553 )
1555 )
1554 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1556 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1555 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1556 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1558 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1557 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1559 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1558
1560
1559 user = relationship('User')
1561 user = relationship('User')
1560 user_group = relationship('UserGroup')
1562 user_group = relationship('UserGroup')
1561 permission = relationship('Permission')
1563 permission = relationship('Permission')
1562
1564
1563 @classmethod
1565 @classmethod
1564 def create(cls, user, user_group, permission):
1566 def create(cls, user, user_group, permission):
1565 n = cls()
1567 n = cls()
1566 n.user = user
1568 n.user = user
1567 n.user_group = user_group
1569 n.user_group = user_group
1568 n.permission = permission
1570 n.permission = permission
1569 Session().add(n)
1571 Session().add(n)
1570 return n
1572 return n
1571
1573
1572 def __unicode__(self):
1574 def __unicode__(self):
1573 return u'<%s => %s >' % (self.user, self.user_group)
1575 return u'<%s => %s >' % (self.user, self.user_group)
1574
1576
1575
1577
1576 class UserToPerm(Base, BaseModel):
1578 class UserToPerm(Base, BaseModel):
1577 __tablename__ = 'user_to_perm'
1579 __tablename__ = 'user_to_perm'
1578 __table_args__ = (
1580 __table_args__ = (
1579 UniqueConstraint('user_id', 'permission_id'),
1581 UniqueConstraint('user_id', 'permission_id'),
1580 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1582 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1581 'mysql_charset': 'utf8'}
1583 'mysql_charset': 'utf8'}
1582 )
1584 )
1583 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1585 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1584 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1586 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1585 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1587 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1586
1588
1587 user = relationship('User')
1589 user = relationship('User')
1588 permission = relationship('Permission', lazy='joined')
1590 permission = relationship('Permission', lazy='joined')
1589
1591
1590 def __unicode__(self):
1592 def __unicode__(self):
1591 return u'<%s => %s >' % (self.user, self.permission)
1593 return u'<%s => %s >' % (self.user, self.permission)
1592
1594
1593
1595
1594 class UserGroupRepoToPerm(Base, BaseModel):
1596 class UserGroupRepoToPerm(Base, BaseModel):
1595 __tablename__ = 'users_group_repo_to_perm'
1597 __tablename__ = 'users_group_repo_to_perm'
1596 __table_args__ = (
1598 __table_args__ = (
1597 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1599 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1598 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1600 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1599 'mysql_charset': 'utf8'}
1601 'mysql_charset': 'utf8'}
1600 )
1602 )
1601 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1603 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1602 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1604 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1603 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1605 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1604 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1606 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1605
1607
1606 users_group = relationship('UserGroup')
1608 users_group = relationship('UserGroup')
1607 permission = relationship('Permission')
1609 permission = relationship('Permission')
1608 repository = relationship('Repository')
1610 repository = relationship('Repository')
1609
1611
1610 @classmethod
1612 @classmethod
1611 def create(cls, users_group, repository, permission):
1613 def create(cls, users_group, repository, permission):
1612 n = cls()
1614 n = cls()
1613 n.users_group = users_group
1615 n.users_group = users_group
1614 n.repository = repository
1616 n.repository = repository
1615 n.permission = permission
1617 n.permission = permission
1616 Session().add(n)
1618 Session().add(n)
1617 return n
1619 return n
1618
1620
1619 def __unicode__(self):
1621 def __unicode__(self):
1620 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1622 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1621
1623
1622
1624
1623 #TODO; not sure if this will be ever used
1624 class UserGroupUserGroupToPerm(Base, BaseModel):
1625 class UserGroupUserGroupToPerm(Base, BaseModel):
1625 __tablename__ = 'user_group_user_group_to_perm'
1626 __tablename__ = 'user_group_user_group_to_perm'
1626 __table_args__ = (
1627 __table_args__ = (
1627 UniqueConstraint('user_group_id', 'user_group_id', 'permission_id'),
1628 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1629 CheckConstraint('target_user_group_id != user_group_id'),
1628 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1630 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1629 'mysql_charset': 'utf8'}
1631 'mysql_charset': 'utf8'}
1630 )
1632 )
1631 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1633 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1632 target_user_group_id = Column("target_users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1634 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1633 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1635 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1634 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1636 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1635
1637
1636 target_user_group = relationship('UserGroup', remote_side=target_user_group_id, primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1638 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1637 user_group = relationship('UserGroup', remote_side=user_group_id, primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1639 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1638 permission = relationship('Permission')
1640 permission = relationship('Permission')
1639
1641
1640 @classmethod
1642 @classmethod
1641 def create(cls, target_user_group, user_group, permission):
1643 def create(cls, target_user_group, user_group, permission):
1642 n = cls()
1644 n = cls()
1643 n.target_user_group = target_user_group
1645 n.target_user_group = target_user_group
1644 n.user_group = user_group
1646 n.user_group = user_group
1645 n.permission = permission
1647 n.permission = permission
1646 Session().add(n)
1648 Session().add(n)
1647 return n
1649 return n
1648
1650
1649 def __unicode__(self):
1651 def __unicode__(self):
1650 return u'<UserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1652 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1651
1653
1652
1654
1653 class UserGroupToPerm(Base, BaseModel):
1655 class UserGroupToPerm(Base, BaseModel):
1654 __tablename__ = 'users_group_to_perm'
1656 __tablename__ = 'users_group_to_perm'
1655 __table_args__ = (
1657 __table_args__ = (
1656 UniqueConstraint('users_group_id', 'permission_id',),
1658 UniqueConstraint('users_group_id', 'permission_id',),
1657 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1659 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1658 'mysql_charset': 'utf8'}
1660 'mysql_charset': 'utf8'}
1659 )
1661 )
1660 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1662 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1661 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1663 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1662 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1664 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1663
1665
1664 users_group = relationship('UserGroup')
1666 users_group = relationship('UserGroup')
1665 permission = relationship('Permission')
1667 permission = relationship('Permission')
1666
1668
1667
1669
1668 class UserRepoGroupToPerm(Base, BaseModel):
1670 class UserRepoGroupToPerm(Base, BaseModel):
1669 __tablename__ = 'user_repo_group_to_perm'
1671 __tablename__ = 'user_repo_group_to_perm'
1670 __table_args__ = (
1672 __table_args__ = (
1671 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1673 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1672 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1673 'mysql_charset': 'utf8'}
1675 'mysql_charset': 'utf8'}
1674 )
1676 )
1675
1677
1676 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1678 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1677 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1679 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1678 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1680 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1679 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1681 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1680
1682
1681 user = relationship('User')
1683 user = relationship('User')
1682 group = relationship('RepoGroup')
1684 group = relationship('RepoGroup')
1683 permission = relationship('Permission')
1685 permission = relationship('Permission')
1684
1686
1685
1687
1686 class UserGroupRepoGroupToPerm(Base, BaseModel):
1688 class UserGroupRepoGroupToPerm(Base, BaseModel):
1687 __tablename__ = 'users_group_repo_group_to_perm'
1689 __tablename__ = 'users_group_repo_group_to_perm'
1688 __table_args__ = (
1690 __table_args__ = (
1689 UniqueConstraint('users_group_id', 'group_id'),
1691 UniqueConstraint('users_group_id', 'group_id'),
1690 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1692 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1691 'mysql_charset': 'utf8'}
1693 'mysql_charset': 'utf8'}
1692 )
1694 )
1693
1695
1694 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1696 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1695 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1697 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1696 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1698 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1697 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1699 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1698
1700
1699 users_group = relationship('UserGroup')
1701 users_group = relationship('UserGroup')
1700 permission = relationship('Permission')
1702 permission = relationship('Permission')
1701 group = relationship('RepoGroup')
1703 group = relationship('RepoGroup')
1702
1704
1703
1705
1704 class Statistics(Base, BaseModel):
1706 class Statistics(Base, BaseModel):
1705 __tablename__ = 'statistics'
1707 __tablename__ = 'statistics'
1706 __table_args__ = (
1708 __table_args__ = (
1707 UniqueConstraint('repository_id'),
1709 UniqueConstraint('repository_id'),
1708 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1710 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1709 'mysql_charset': 'utf8'}
1711 'mysql_charset': 'utf8'}
1710 )
1712 )
1711 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1713 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1712 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1714 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1713 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1715 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1714 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1716 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1715 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1717 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1716 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1718 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1717
1719
1718 repository = relationship('Repository', single_parent=True)
1720 repository = relationship('Repository', single_parent=True)
1719
1721
1720
1722
1721 class UserFollowing(Base, BaseModel):
1723 class UserFollowing(Base, BaseModel):
1722 __tablename__ = 'user_followings'
1724 __tablename__ = 'user_followings'
1723 __table_args__ = (
1725 __table_args__ = (
1724 UniqueConstraint('user_id', 'follows_repository_id'),
1726 UniqueConstraint('user_id', 'follows_repository_id'),
1725 UniqueConstraint('user_id', 'follows_user_id'),
1727 UniqueConstraint('user_id', 'follows_user_id'),
1726 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1728 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1727 'mysql_charset': 'utf8'}
1729 'mysql_charset': 'utf8'}
1728 )
1730 )
1729
1731
1730 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1732 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1731 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1733 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1732 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1734 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1733 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1735 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1734 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1736 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1735
1737
1736 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1738 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1737
1739
1738 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1740 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1739 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1741 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1740
1742
1741 @classmethod
1743 @classmethod
1742 def get_repo_followers(cls, repo_id):
1744 def get_repo_followers(cls, repo_id):
1743 return cls.query().filter(cls.follows_repo_id == repo_id)
1745 return cls.query().filter(cls.follows_repo_id == repo_id)
1744
1746
1745
1747
1746 class CacheInvalidation(Base, BaseModel):
1748 class CacheInvalidation(Base, BaseModel):
1747 __tablename__ = 'cache_invalidation'
1749 __tablename__ = 'cache_invalidation'
1748 __table_args__ = (
1750 __table_args__ = (
1749 UniqueConstraint('cache_key'),
1751 UniqueConstraint('cache_key'),
1750 Index('key_idx', 'cache_key'),
1752 Index('key_idx', 'cache_key'),
1751 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1753 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1752 'mysql_charset': 'utf8'},
1754 'mysql_charset': 'utf8'},
1753 )
1755 )
1754 # cache_id, not used
1756 # cache_id, not used
1755 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1757 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1756 # cache_key as created by _get_cache_key
1758 # cache_key as created by _get_cache_key
1757 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1759 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1758 # cache_args is a repo_name
1760 # cache_args is a repo_name
1759 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1761 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1760 # instance sets cache_active True when it is caching,
1762 # instance sets cache_active True when it is caching,
1761 # other instances set cache_active to False to indicate that this cache is invalid
1763 # other instances set cache_active to False to indicate that this cache is invalid
1762 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1764 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1763
1765
1764 def __init__(self, cache_key, repo_name=''):
1766 def __init__(self, cache_key, repo_name=''):
1765 self.cache_key = cache_key
1767 self.cache_key = cache_key
1766 self.cache_args = repo_name
1768 self.cache_args = repo_name
1767 self.cache_active = False
1769 self.cache_active = False
1768
1770
1769 def __unicode__(self):
1771 def __unicode__(self):
1770 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1772 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1771 self.cache_id, self.cache_key, self.cache_active)
1773 self.cache_id, self.cache_key, self.cache_active)
1772
1774
1773 def _cache_key_partition(self):
1775 def _cache_key_partition(self):
1774 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1776 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1775 return prefix, repo_name, suffix
1777 return prefix, repo_name, suffix
1776
1778
1777 def get_prefix(self):
1779 def get_prefix(self):
1778 """
1780 """
1779 get prefix that might have been used in _get_cache_key to
1781 get prefix that might have been used in _get_cache_key to
1780 generate self.cache_key. Only used for informational purposes
1782 generate self.cache_key. Only used for informational purposes
1781 in repo_edit.html.
1783 in repo_edit.html.
1782 """
1784 """
1783 # prefix, repo_name, suffix
1785 # prefix, repo_name, suffix
1784 return self._cache_key_partition()[0]
1786 return self._cache_key_partition()[0]
1785
1787
1786 def get_suffix(self):
1788 def get_suffix(self):
1787 """
1789 """
1788 get suffix that might have been used in _get_cache_key to
1790 get suffix that might have been used in _get_cache_key to
1789 generate self.cache_key. Only used for informational purposes
1791 generate self.cache_key. Only used for informational purposes
1790 in repo_edit.html.
1792 in repo_edit.html.
1791 """
1793 """
1792 # prefix, repo_name, suffix
1794 # prefix, repo_name, suffix
1793 return self._cache_key_partition()[2]
1795 return self._cache_key_partition()[2]
1794
1796
1795 @classmethod
1797 @classmethod
1796 def clear_cache(cls):
1798 def clear_cache(cls):
1797 """
1799 """
1798 Delete all cache keys from database.
1800 Delete all cache keys from database.
1799 Should only be run when all instances are down and all entries thus stale.
1801 Should only be run when all instances are down and all entries thus stale.
1800 """
1802 """
1801 cls.query().delete()
1803 cls.query().delete()
1802 Session().commit()
1804 Session().commit()
1803
1805
1804 @classmethod
1806 @classmethod
1805 def _get_cache_key(cls, key):
1807 def _get_cache_key(cls, key):
1806 """
1808 """
1807 Wrapper for generating a unique cache key for this instance and "key".
1809 Wrapper for generating a unique cache key for this instance and "key".
1808 key must / will start with a repo_name which will be stored in .cache_args .
1810 key must / will start with a repo_name which will be stored in .cache_args .
1809 """
1811 """
1810 import rhodecode
1812 import rhodecode
1811 prefix = rhodecode.CONFIG.get('instance_id', '')
1813 prefix = rhodecode.CONFIG.get('instance_id', '')
1812 return "%s%s" % (prefix, key)
1814 return "%s%s" % (prefix, key)
1813
1815
1814 @classmethod
1816 @classmethod
1815 def set_invalidate(cls, repo_name):
1817 def set_invalidate(cls, repo_name):
1816 """
1818 """
1817 Mark all caches of a repo as invalid in the database.
1819 Mark all caches of a repo as invalid in the database.
1818 """
1820 """
1819 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1821 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1820
1822
1821 try:
1823 try:
1822 for inv_obj in inv_objs:
1824 for inv_obj in inv_objs:
1823 log.debug('marking %s key for invalidation based on repo_name=%s'
1825 log.debug('marking %s key for invalidation based on repo_name=%s'
1824 % (inv_obj, safe_str(repo_name)))
1826 % (inv_obj, safe_str(repo_name)))
1825 inv_obj.cache_active = False
1827 inv_obj.cache_active = False
1826 Session().add(inv_obj)
1828 Session().add(inv_obj)
1827 Session().commit()
1829 Session().commit()
1828 except Exception:
1830 except Exception:
1829 log.error(traceback.format_exc())
1831 log.error(traceback.format_exc())
1830 Session().rollback()
1832 Session().rollback()
1831
1833
1832 @classmethod
1834 @classmethod
1833 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1835 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1834 """
1836 """
1835 Mark this cache key as active and currently cached.
1837 Mark this cache key as active and currently cached.
1836 Return True if the existing cache registration still was valid.
1838 Return True if the existing cache registration still was valid.
1837 Return False to indicate that it had been invalidated and caches should be refreshed.
1839 Return False to indicate that it had been invalidated and caches should be refreshed.
1838 """
1840 """
1839
1841
1840 key = (repo_name + '_' + kind) if kind else repo_name
1842 key = (repo_name + '_' + kind) if kind else repo_name
1841 cache_key = cls._get_cache_key(key)
1843 cache_key = cls._get_cache_key(key)
1842
1844
1843 if valid_cache_keys and cache_key in valid_cache_keys:
1845 if valid_cache_keys and cache_key in valid_cache_keys:
1844 return True
1846 return True
1845
1847
1846 try:
1848 try:
1847 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1849 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1848 if not inv_obj:
1850 if not inv_obj:
1849 inv_obj = CacheInvalidation(cache_key, repo_name)
1851 inv_obj = CacheInvalidation(cache_key, repo_name)
1850 was_valid = inv_obj.cache_active
1852 was_valid = inv_obj.cache_active
1851 inv_obj.cache_active = True
1853 inv_obj.cache_active = True
1852 Session().add(inv_obj)
1854 Session().add(inv_obj)
1853 Session().commit()
1855 Session().commit()
1854 return was_valid
1856 return was_valid
1855 except Exception:
1857 except Exception:
1856 log.error(traceback.format_exc())
1858 log.error(traceback.format_exc())
1857 Session().rollback()
1859 Session().rollback()
1858 return False
1860 return False
1859
1861
1860 @classmethod
1862 @classmethod
1861 def get_valid_cache_keys(cls):
1863 def get_valid_cache_keys(cls):
1862 """
1864 """
1863 Return opaque object with information of which caches still are valid
1865 Return opaque object with information of which caches still are valid
1864 and can be used without checking for invalidation.
1866 and can be used without checking for invalidation.
1865 """
1867 """
1866 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1868 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1867
1869
1868
1870
1869 class ChangesetComment(Base, BaseModel):
1871 class ChangesetComment(Base, BaseModel):
1870 __tablename__ = 'changeset_comments'
1872 __tablename__ = 'changeset_comments'
1871 __table_args__ = (
1873 __table_args__ = (
1872 Index('cc_revision_idx', 'revision'),
1874 Index('cc_revision_idx', 'revision'),
1873 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1875 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1874 'mysql_charset': 'utf8'},
1876 'mysql_charset': 'utf8'},
1875 )
1877 )
1876 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1878 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1877 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1879 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1878 revision = Column('revision', String(40), nullable=True)
1880 revision = Column('revision', String(40), nullable=True)
1879 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1881 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1880 line_no = Column('line_no', Unicode(10), nullable=True)
1882 line_no = Column('line_no', Unicode(10), nullable=True)
1881 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1883 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1882 f_path = Column('f_path', Unicode(1000), nullable=True)
1884 f_path = Column('f_path', Unicode(1000), nullable=True)
1883 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1885 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1884 text = Column('text', UnicodeText(25000), nullable=False)
1886 text = Column('text', UnicodeText(25000), nullable=False)
1885 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1887 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1886 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1888 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1887
1889
1888 author = relationship('User', lazy='joined')
1890 author = relationship('User', lazy='joined')
1889 repo = relationship('Repository')
1891 repo = relationship('Repository')
1890 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1892 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1891 pull_request = relationship('PullRequest', lazy='joined')
1893 pull_request = relationship('PullRequest', lazy='joined')
1892
1894
1893 @classmethod
1895 @classmethod
1894 def get_users(cls, revision=None, pull_request_id=None):
1896 def get_users(cls, revision=None, pull_request_id=None):
1895 """
1897 """
1896 Returns user associated with this ChangesetComment. ie those
1898 Returns user associated with this ChangesetComment. ie those
1897 who actually commented
1899 who actually commented
1898
1900
1899 :param cls:
1901 :param cls:
1900 :param revision:
1902 :param revision:
1901 """
1903 """
1902 q = Session().query(User)\
1904 q = Session().query(User)\
1903 .join(ChangesetComment.author)
1905 .join(ChangesetComment.author)
1904 if revision:
1906 if revision:
1905 q = q.filter(cls.revision == revision)
1907 q = q.filter(cls.revision == revision)
1906 elif pull_request_id:
1908 elif pull_request_id:
1907 q = q.filter(cls.pull_request_id == pull_request_id)
1909 q = q.filter(cls.pull_request_id == pull_request_id)
1908 return q.all()
1910 return q.all()
1909
1911
1910
1912
1911 class ChangesetStatus(Base, BaseModel):
1913 class ChangesetStatus(Base, BaseModel):
1912 __tablename__ = 'changeset_statuses'
1914 __tablename__ = 'changeset_statuses'
1913 __table_args__ = (
1915 __table_args__ = (
1914 Index('cs_revision_idx', 'revision'),
1916 Index('cs_revision_idx', 'revision'),
1915 Index('cs_version_idx', 'version'),
1917 Index('cs_version_idx', 'version'),
1916 UniqueConstraint('repo_id', 'revision', 'version'),
1918 UniqueConstraint('repo_id', 'revision', 'version'),
1917 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1919 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1918 'mysql_charset': 'utf8'}
1920 'mysql_charset': 'utf8'}
1919 )
1921 )
1920 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1922 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1921 STATUS_APPROVED = 'approved'
1923 STATUS_APPROVED = 'approved'
1922 STATUS_REJECTED = 'rejected'
1924 STATUS_REJECTED = 'rejected'
1923 STATUS_UNDER_REVIEW = 'under_review'
1925 STATUS_UNDER_REVIEW = 'under_review'
1924
1926
1925 STATUSES = [
1927 STATUSES = [
1926 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1928 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1927 (STATUS_APPROVED, _("Approved")),
1929 (STATUS_APPROVED, _("Approved")),
1928 (STATUS_REJECTED, _("Rejected")),
1930 (STATUS_REJECTED, _("Rejected")),
1929 (STATUS_UNDER_REVIEW, _("Under Review")),
1931 (STATUS_UNDER_REVIEW, _("Under Review")),
1930 ]
1932 ]
1931
1933
1932 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1934 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1933 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1935 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1934 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1936 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1935 revision = Column('revision', String(40), nullable=False)
1937 revision = Column('revision', String(40), nullable=False)
1936 status = Column('status', String(128), nullable=False, default=DEFAULT)
1938 status = Column('status', String(128), nullable=False, default=DEFAULT)
1937 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1939 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1938 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1940 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1939 version = Column('version', Integer(), nullable=False, default=0)
1941 version = Column('version', Integer(), nullable=False, default=0)
1940 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1942 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1941
1943
1942 author = relationship('User', lazy='joined')
1944 author = relationship('User', lazy='joined')
1943 repo = relationship('Repository')
1945 repo = relationship('Repository')
1944 comment = relationship('ChangesetComment', lazy='joined')
1946 comment = relationship('ChangesetComment', lazy='joined')
1945 pull_request = relationship('PullRequest', lazy='joined')
1947 pull_request = relationship('PullRequest', lazy='joined')
1946
1948
1947 def __unicode__(self):
1949 def __unicode__(self):
1948 return u"<%s('%s:%s')>" % (
1950 return u"<%s('%s:%s')>" % (
1949 self.__class__.__name__,
1951 self.__class__.__name__,
1950 self.status, self.author
1952 self.status, self.author
1951 )
1953 )
1952
1954
1953 @classmethod
1955 @classmethod
1954 def get_status_lbl(cls, value):
1956 def get_status_lbl(cls, value):
1955 return dict(cls.STATUSES).get(value)
1957 return dict(cls.STATUSES).get(value)
1956
1958
1957 @property
1959 @property
1958 def status_lbl(self):
1960 def status_lbl(self):
1959 return ChangesetStatus.get_status_lbl(self.status)
1961 return ChangesetStatus.get_status_lbl(self.status)
1960
1962
1961
1963
1962 class PullRequest(Base, BaseModel):
1964 class PullRequest(Base, BaseModel):
1963 __tablename__ = 'pull_requests'
1965 __tablename__ = 'pull_requests'
1964 __table_args__ = (
1966 __table_args__ = (
1965 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1967 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1966 'mysql_charset': 'utf8'},
1968 'mysql_charset': 'utf8'},
1967 )
1969 )
1968
1970
1969 STATUS_NEW = u'new'
1971 STATUS_NEW = u'new'
1970 STATUS_OPEN = u'open'
1972 STATUS_OPEN = u'open'
1971 STATUS_CLOSED = u'closed'
1973 STATUS_CLOSED = u'closed'
1972
1974
1973 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1975 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1974 title = Column('title', Unicode(256), nullable=True)
1976 title = Column('title', Unicode(256), nullable=True)
1975 description = Column('description', UnicodeText(10240), nullable=True)
1977 description = Column('description', UnicodeText(10240), nullable=True)
1976 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1978 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1977 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1979 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1978 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1980 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1979 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1980 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1982 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1981 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1983 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1982 org_ref = Column('org_ref', Unicode(256), nullable=False)
1984 org_ref = Column('org_ref', Unicode(256), nullable=False)
1983 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1985 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1984 other_ref = Column('other_ref', Unicode(256), nullable=False)
1986 other_ref = Column('other_ref', Unicode(256), nullable=False)
1985
1987
1986 @hybrid_property
1988 @hybrid_property
1987 def revisions(self):
1989 def revisions(self):
1988 return self._revisions.split(':')
1990 return self._revisions.split(':')
1989
1991
1990 @revisions.setter
1992 @revisions.setter
1991 def revisions(self, val):
1993 def revisions(self, val):
1992 self._revisions = ':'.join(val)
1994 self._revisions = ':'.join(val)
1993
1995
1994 @property
1996 @property
1995 def org_ref_parts(self):
1997 def org_ref_parts(self):
1996 return self.org_ref.split(':')
1998 return self.org_ref.split(':')
1997
1999
1998 @property
2000 @property
1999 def other_ref_parts(self):
2001 def other_ref_parts(self):
2000 return self.other_ref.split(':')
2002 return self.other_ref.split(':')
2001
2003
2002 author = relationship('User', lazy='joined')
2004 author = relationship('User', lazy='joined')
2003 reviewers = relationship('PullRequestReviewers',
2005 reviewers = relationship('PullRequestReviewers',
2004 cascade="all, delete, delete-orphan")
2006 cascade="all, delete, delete-orphan")
2005 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2007 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2006 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2008 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2007 statuses = relationship('ChangesetStatus')
2009 statuses = relationship('ChangesetStatus')
2008 comments = relationship('ChangesetComment',
2010 comments = relationship('ChangesetComment',
2009 cascade="all, delete, delete-orphan")
2011 cascade="all, delete, delete-orphan")
2010
2012
2011 def is_closed(self):
2013 def is_closed(self):
2012 return self.status == self.STATUS_CLOSED
2014 return self.status == self.STATUS_CLOSED
2013
2015
2014 @property
2016 @property
2015 def last_review_status(self):
2017 def last_review_status(self):
2016 return self.statuses[-1].status if self.statuses else ''
2018 return self.statuses[-1].status if self.statuses else ''
2017
2019
2018 def __json__(self):
2020 def __json__(self):
2019 return dict(
2021 return dict(
2020 revisions=self.revisions
2022 revisions=self.revisions
2021 )
2023 )
2022
2024
2023
2025
2024 class PullRequestReviewers(Base, BaseModel):
2026 class PullRequestReviewers(Base, BaseModel):
2025 __tablename__ = 'pull_request_reviewers'
2027 __tablename__ = 'pull_request_reviewers'
2026 __table_args__ = (
2028 __table_args__ = (
2027 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2029 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2028 'mysql_charset': 'utf8'},
2030 'mysql_charset': 'utf8'},
2029 )
2031 )
2030
2032
2031 def __init__(self, user=None, pull_request=None):
2033 def __init__(self, user=None, pull_request=None):
2032 self.user = user
2034 self.user = user
2033 self.pull_request = pull_request
2035 self.pull_request = pull_request
2034
2036
2035 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2037 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2036 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2038 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2037 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2039 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2038
2040
2039 user = relationship('User')
2041 user = relationship('User')
2040 pull_request = relationship('PullRequest')
2042 pull_request = relationship('PullRequest')
2041
2043
2042
2044
2043 class Notification(Base, BaseModel):
2045 class Notification(Base, BaseModel):
2044 __tablename__ = 'notifications'
2046 __tablename__ = 'notifications'
2045 __table_args__ = (
2047 __table_args__ = (
2046 Index('notification_type_idx', 'type'),
2048 Index('notification_type_idx', 'type'),
2047 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2049 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2048 'mysql_charset': 'utf8'},
2050 'mysql_charset': 'utf8'},
2049 )
2051 )
2050
2052
2051 TYPE_CHANGESET_COMMENT = u'cs_comment'
2053 TYPE_CHANGESET_COMMENT = u'cs_comment'
2052 TYPE_MESSAGE = u'message'
2054 TYPE_MESSAGE = u'message'
2053 TYPE_MENTION = u'mention'
2055 TYPE_MENTION = u'mention'
2054 TYPE_REGISTRATION = u'registration'
2056 TYPE_REGISTRATION = u'registration'
2055 TYPE_PULL_REQUEST = u'pull_request'
2057 TYPE_PULL_REQUEST = u'pull_request'
2056 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2058 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2057
2059
2058 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2060 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2059 subject = Column('subject', Unicode(512), nullable=True)
2061 subject = Column('subject', Unicode(512), nullable=True)
2060 body = Column('body', UnicodeText(50000), nullable=True)
2062 body = Column('body', UnicodeText(50000), nullable=True)
2061 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2063 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2062 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2064 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2063 type_ = Column('type', Unicode(256))
2065 type_ = Column('type', Unicode(256))
2064
2066
2065 created_by_user = relationship('User')
2067 created_by_user = relationship('User')
2066 notifications_to_users = relationship('UserNotification', lazy='joined',
2068 notifications_to_users = relationship('UserNotification', lazy='joined',
2067 cascade="all, delete, delete-orphan")
2069 cascade="all, delete, delete-orphan")
2068
2070
2069 @property
2071 @property
2070 def recipients(self):
2072 def recipients(self):
2071 return [x.user for x in UserNotification.query()\
2073 return [x.user for x in UserNotification.query()\
2072 .filter(UserNotification.notification == self)\
2074 .filter(UserNotification.notification == self)\
2073 .order_by(UserNotification.user_id.asc()).all()]
2075 .order_by(UserNotification.user_id.asc()).all()]
2074
2076
2075 @classmethod
2077 @classmethod
2076 def create(cls, created_by, subject, body, recipients, type_=None):
2078 def create(cls, created_by, subject, body, recipients, type_=None):
2077 if type_ is None:
2079 if type_ is None:
2078 type_ = Notification.TYPE_MESSAGE
2080 type_ = Notification.TYPE_MESSAGE
2079
2081
2080 notification = cls()
2082 notification = cls()
2081 notification.created_by_user = created_by
2083 notification.created_by_user = created_by
2082 notification.subject = subject
2084 notification.subject = subject
2083 notification.body = body
2085 notification.body = body
2084 notification.type_ = type_
2086 notification.type_ = type_
2085 notification.created_on = datetime.datetime.now()
2087 notification.created_on = datetime.datetime.now()
2086
2088
2087 for u in recipients:
2089 for u in recipients:
2088 assoc = UserNotification()
2090 assoc = UserNotification()
2089 assoc.notification = notification
2091 assoc.notification = notification
2090 u.notifications.append(assoc)
2092 u.notifications.append(assoc)
2091 Session().add(notification)
2093 Session().add(notification)
2092 return notification
2094 return notification
2093
2095
2094 @property
2096 @property
2095 def description(self):
2097 def description(self):
2096 from rhodecode.model.notification import NotificationModel
2098 from rhodecode.model.notification import NotificationModel
2097 return NotificationModel().make_description(self)
2099 return NotificationModel().make_description(self)
2098
2100
2099
2101
2100 class UserNotification(Base, BaseModel):
2102 class UserNotification(Base, BaseModel):
2101 __tablename__ = 'user_to_notification'
2103 __tablename__ = 'user_to_notification'
2102 __table_args__ = (
2104 __table_args__ = (
2103 UniqueConstraint('user_id', 'notification_id'),
2105 UniqueConstraint('user_id', 'notification_id'),
2104 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2106 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2105 'mysql_charset': 'utf8'}
2107 'mysql_charset': 'utf8'}
2106 )
2108 )
2107 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2109 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2108 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2110 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2109 read = Column('read', Boolean, default=False)
2111 read = Column('read', Boolean, default=False)
2110 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2112 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2111
2113
2112 user = relationship('User', lazy="joined")
2114 user = relationship('User', lazy="joined")
2113 notification = relationship('Notification', lazy="joined",
2115 notification = relationship('Notification', lazy="joined",
2114 order_by=lambda: Notification.created_on.desc(),)
2116 order_by=lambda: Notification.created_on.desc(),)
2115
2117
2116 def mark_as_read(self):
2118 def mark_as_read(self):
2117 self.read = True
2119 self.read = True
2118 Session().add(self)
2120 Session().add(self)
2119
2121
2120
2122
2121 class DbMigrateVersion(Base, BaseModel):
2123 class DbMigrateVersion(Base, BaseModel):
2122 __tablename__ = 'db_migrate_version'
2124 __tablename__ = 'db_migrate_version'
2123 __table_args__ = (
2125 __table_args__ = (
2124 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2125 'mysql_charset': 'utf8'},
2127 'mysql_charset': 'utf8'},
2126 )
2128 )
2127 repository_id = Column('repository_id', String(250), primary_key=True)
2129 repository_id = Column('repository_id', String(250), primary_key=True)
2128 repository_path = Column('repository_path', Text)
2130 repository_path = Column('repository_path', Text)
2129 version = Column('version', Integer)
2131 version = Column('version', Integer)
@@ -1,782 +1,803 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import itertools
28 import itertools
29 import collections
29 import collections
30 from pylons import url
30 from pylons import url
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35
35
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
37 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
42 UserEmailMap, UserIpMap
42 UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
43 from rhodecode.lib.exceptions import DefaultUserException, \
43 from rhodecode.lib.exceptions import DefaultUserException, \
44 UserOwnsReposException
44 UserOwnsReposException
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51
51
52
52
53 class UserModel(BaseModel):
53 class UserModel(BaseModel):
54 cls = User
54 cls = User
55
55
56 def get(self, user_id, cache=False):
56 def get(self, user_id, cache=False):
57 user = self.sa.query(User)
57 user = self.sa.query(User)
58 if cache:
58 if cache:
59 user = user.options(FromCache("sql_cache_short",
59 user = user.options(FromCache("sql_cache_short",
60 "get_user_%s" % user_id))
60 "get_user_%s" % user_id))
61 return user.get(user_id)
61 return user.get(user_id)
62
62
63 def get_user(self, user):
63 def get_user(self, user):
64 return self._get_user(user)
64 return self._get_user(user)
65
65
66 def get_by_username(self, username, cache=False, case_insensitive=False):
66 def get_by_username(self, username, cache=False, case_insensitive=False):
67
67
68 if case_insensitive:
68 if case_insensitive:
69 user = self.sa.query(User).filter(User.username.ilike(username))
69 user = self.sa.query(User).filter(User.username.ilike(username))
70 else:
70 else:
71 user = self.sa.query(User)\
71 user = self.sa.query(User)\
72 .filter(User.username == username)
72 .filter(User.username == username)
73 if cache:
73 if cache:
74 user = user.options(FromCache("sql_cache_short",
74 user = user.options(FromCache("sql_cache_short",
75 "get_user_%s" % username))
75 "get_user_%s" % username))
76 return user.scalar()
76 return user.scalar()
77
77
78 def get_by_email(self, email, cache=False, case_insensitive=False):
78 def get_by_email(self, email, cache=False, case_insensitive=False):
79 return User.get_by_email(email, case_insensitive, cache)
79 return User.get_by_email(email, case_insensitive, cache)
80
80
81 def get_by_api_key(self, api_key, cache=False):
81 def get_by_api_key(self, api_key, cache=False):
82 return User.get_by_api_key(api_key, cache)
82 return User.get_by_api_key(api_key, cache)
83
83
84 def create(self, form_data):
84 def create(self, form_data):
85 from rhodecode.lib.auth import get_crypt_password
85 from rhodecode.lib.auth import get_crypt_password
86 try:
86 try:
87 new_user = User()
87 new_user = User()
88 for k, v in form_data.items():
88 for k, v in form_data.items():
89 if k == 'password':
89 if k == 'password':
90 v = get_crypt_password(v)
90 v = get_crypt_password(v)
91 if k == 'firstname':
91 if k == 'firstname':
92 k = 'name'
92 k = 'name'
93 setattr(new_user, k, v)
93 setattr(new_user, k, v)
94
94
95 new_user.api_key = generate_api_key(form_data['username'])
95 new_user.api_key = generate_api_key(form_data['username'])
96 self.sa.add(new_user)
96 self.sa.add(new_user)
97 return new_user
97 return new_user
98 except Exception:
98 except Exception:
99 log.error(traceback.format_exc())
99 log.error(traceback.format_exc())
100 raise
100 raise
101
101
102 def create_or_update(self, username, password, email, firstname='',
102 def create_or_update(self, username, password, email, firstname='',
103 lastname='', active=True, admin=False, ldap_dn=None):
103 lastname='', active=True, admin=False, ldap_dn=None):
104 """
104 """
105 Creates a new instance if not found, or updates current one
105 Creates a new instance if not found, or updates current one
106
106
107 :param username:
107 :param username:
108 :param password:
108 :param password:
109 :param email:
109 :param email:
110 :param active:
110 :param active:
111 :param firstname:
111 :param firstname:
112 :param lastname:
112 :param lastname:
113 :param active:
113 :param active:
114 :param admin:
114 :param admin:
115 :param ldap_dn:
115 :param ldap_dn:
116 """
116 """
117
117
118 from rhodecode.lib.auth import get_crypt_password
118 from rhodecode.lib.auth import get_crypt_password
119
119
120 log.debug('Checking for %s account in RhodeCode database' % username)
120 log.debug('Checking for %s account in RhodeCode database' % username)
121 user = User.get_by_username(username, case_insensitive=True)
121 user = User.get_by_username(username, case_insensitive=True)
122 if user is None:
122 if user is None:
123 log.debug('creating new user %s' % username)
123 log.debug('creating new user %s' % username)
124 new_user = User()
124 new_user = User()
125 edit = False
125 edit = False
126 else:
126 else:
127 log.debug('updating user %s' % username)
127 log.debug('updating user %s' % username)
128 new_user = user
128 new_user = user
129 edit = True
129 edit = True
130
130
131 try:
131 try:
132 new_user.username = username
132 new_user.username = username
133 new_user.admin = admin
133 new_user.admin = admin
134 # set password only if creating an user or password is changed
134 # set password only if creating an user or password is changed
135 if not edit or user.password != password:
135 if not edit or user.password != password:
136 new_user.password = get_crypt_password(password)
136 new_user.password = get_crypt_password(password)
137 new_user.api_key = generate_api_key(username)
137 new_user.api_key = generate_api_key(username)
138 new_user.email = email
138 new_user.email = email
139 new_user.active = active
139 new_user.active = active
140 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
140 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
141 new_user.name = firstname
141 new_user.name = firstname
142 new_user.lastname = lastname
142 new_user.lastname = lastname
143 self.sa.add(new_user)
143 self.sa.add(new_user)
144 return new_user
144 return new_user
145 except (DatabaseError,):
145 except (DatabaseError,):
146 log.error(traceback.format_exc())
146 log.error(traceback.format_exc())
147 raise
147 raise
148
148
149 def create_for_container_auth(self, username, attrs):
149 def create_for_container_auth(self, username, attrs):
150 """
150 """
151 Creates the given user if it's not already in the database
151 Creates the given user if it's not already in the database
152
152
153 :param username:
153 :param username:
154 :param attrs:
154 :param attrs:
155 """
155 """
156 if self.get_by_username(username, case_insensitive=True) is None:
156 if self.get_by_username(username, case_insensitive=True) is None:
157
157
158 # autogenerate email for container account without one
158 # autogenerate email for container account without one
159 generate_email = lambda usr: '%s@container_auth.account' % usr
159 generate_email = lambda usr: '%s@container_auth.account' % usr
160
160
161 try:
161 try:
162 new_user = User()
162 new_user = User()
163 new_user.username = username
163 new_user.username = username
164 new_user.password = None
164 new_user.password = None
165 new_user.api_key = generate_api_key(username)
165 new_user.api_key = generate_api_key(username)
166 new_user.email = attrs['email']
166 new_user.email = attrs['email']
167 new_user.active = attrs.get('active', True)
167 new_user.active = attrs.get('active', True)
168 new_user.name = attrs['name'] or generate_email(username)
168 new_user.name = attrs['name'] or generate_email(username)
169 new_user.lastname = attrs['lastname']
169 new_user.lastname = attrs['lastname']
170
170
171 self.sa.add(new_user)
171 self.sa.add(new_user)
172 return new_user
172 return new_user
173 except (DatabaseError,):
173 except (DatabaseError,):
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 self.sa.rollback()
175 self.sa.rollback()
176 raise
176 raise
177 log.debug('User %s already exists. Skipping creation of account'
177 log.debug('User %s already exists. Skipping creation of account'
178 ' for container auth.', username)
178 ' for container auth.', username)
179 return None
179 return None
180
180
181 def create_ldap(self, username, password, user_dn, attrs):
181 def create_ldap(self, username, password, user_dn, attrs):
182 """
182 """
183 Checks if user is in database, if not creates this user marked
183 Checks if user is in database, if not creates this user marked
184 as ldap user
184 as ldap user
185
185
186 :param username:
186 :param username:
187 :param password:
187 :param password:
188 :param user_dn:
188 :param user_dn:
189 :param attrs:
189 :param attrs:
190 """
190 """
191 from rhodecode.lib.auth import get_crypt_password
191 from rhodecode.lib.auth import get_crypt_password
192 log.debug('Checking for such ldap account in RhodeCode database')
192 log.debug('Checking for such ldap account in RhodeCode database')
193 if self.get_by_username(username, case_insensitive=True) is None:
193 if self.get_by_username(username, case_insensitive=True) is None:
194
194
195 # autogenerate email for ldap account without one
195 # autogenerate email for ldap account without one
196 generate_email = lambda usr: '%s@ldap.account' % usr
196 generate_email = lambda usr: '%s@ldap.account' % usr
197
197
198 try:
198 try:
199 new_user = User()
199 new_user = User()
200 username = username.lower()
200 username = username.lower()
201 # add ldap account always lowercase
201 # add ldap account always lowercase
202 new_user.username = username
202 new_user.username = username
203 new_user.password = get_crypt_password(password)
203 new_user.password = get_crypt_password(password)
204 new_user.api_key = generate_api_key(username)
204 new_user.api_key = generate_api_key(username)
205 new_user.email = attrs['email'] or generate_email(username)
205 new_user.email = attrs['email'] or generate_email(username)
206 new_user.active = attrs.get('active', True)
206 new_user.active = attrs.get('active', True)
207 new_user.ldap_dn = safe_unicode(user_dn)
207 new_user.ldap_dn = safe_unicode(user_dn)
208 new_user.name = attrs['name']
208 new_user.name = attrs['name']
209 new_user.lastname = attrs['lastname']
209 new_user.lastname = attrs['lastname']
210
210
211 self.sa.add(new_user)
211 self.sa.add(new_user)
212 return new_user
212 return new_user
213 except (DatabaseError,):
213 except (DatabaseError,):
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 self.sa.rollback()
215 self.sa.rollback()
216 raise
216 raise
217 log.debug('this %s user exists skipping creation of ldap account',
217 log.debug('this %s user exists skipping creation of ldap account',
218 username)
218 username)
219 return None
219 return None
220
220
221 def create_registration(self, form_data):
221 def create_registration(self, form_data):
222 from rhodecode.model.notification import NotificationModel
222 from rhodecode.model.notification import NotificationModel
223
223
224 try:
224 try:
225 form_data['admin'] = False
225 form_data['admin'] = False
226 new_user = self.create(form_data)
226 new_user = self.create(form_data)
227
227
228 self.sa.add(new_user)
228 self.sa.add(new_user)
229 self.sa.flush()
229 self.sa.flush()
230
230
231 # notification to admins
231 # notification to admins
232 subject = _('New user registration')
232 subject = _('New user registration')
233 body = ('New user registration\n'
233 body = ('New user registration\n'
234 '---------------------\n'
234 '---------------------\n'
235 '- Username: %s\n'
235 '- Username: %s\n'
236 '- Full Name: %s\n'
236 '- Full Name: %s\n'
237 '- Email: %s\n')
237 '- Email: %s\n')
238 body = body % (new_user.username, new_user.full_name,
238 body = body % (new_user.username, new_user.full_name,
239 new_user.email)
239 new_user.email)
240 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
240 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
241 kw = {'registered_user_url': edit_url}
241 kw = {'registered_user_url': edit_url}
242 NotificationModel().create(created_by=new_user, subject=subject,
242 NotificationModel().create(created_by=new_user, subject=subject,
243 body=body, recipients=None,
243 body=body, recipients=None,
244 type_=Notification.TYPE_REGISTRATION,
244 type_=Notification.TYPE_REGISTRATION,
245 email_kwargs=kw)
245 email_kwargs=kw)
246
246
247 except Exception:
247 except Exception:
248 log.error(traceback.format_exc())
248 log.error(traceback.format_exc())
249 raise
249 raise
250
250
251 def update(self, user_id, form_data, skip_attrs=[]):
251 def update(self, user_id, form_data, skip_attrs=[]):
252 from rhodecode.lib.auth import get_crypt_password
252 from rhodecode.lib.auth import get_crypt_password
253 try:
253 try:
254 user = self.get(user_id, cache=False)
254 user = self.get(user_id, cache=False)
255 if user.username == 'default':
255 if user.username == 'default':
256 raise DefaultUserException(
256 raise DefaultUserException(
257 _("You can't Edit this user since it's"
257 _("You can't Edit this user since it's"
258 " crucial for entire application"))
258 " crucial for entire application"))
259
259
260 for k, v in form_data.items():
260 for k, v in form_data.items():
261 if k in skip_attrs:
261 if k in skip_attrs:
262 continue
262 continue
263 if k == 'new_password' and v:
263 if k == 'new_password' and v:
264 user.password = get_crypt_password(v)
264 user.password = get_crypt_password(v)
265 user.api_key = generate_api_key(user.username)
265 user.api_key = generate_api_key(user.username)
266 else:
266 else:
267 if k == 'firstname':
267 if k == 'firstname':
268 k = 'name'
268 k = 'name'
269 setattr(user, k, v)
269 setattr(user, k, v)
270 self.sa.add(user)
270 self.sa.add(user)
271 except Exception:
271 except Exception:
272 log.error(traceback.format_exc())
272 log.error(traceback.format_exc())
273 raise
273 raise
274
274
275 def update_user(self, user, **kwargs):
275 def update_user(self, user, **kwargs):
276 from rhodecode.lib.auth import get_crypt_password
276 from rhodecode.lib.auth import get_crypt_password
277 try:
277 try:
278 user = self._get_user(user)
278 user = self._get_user(user)
279 if user.username == 'default':
279 if user.username == 'default':
280 raise DefaultUserException(
280 raise DefaultUserException(
281 _("You can't Edit this user since it's"
281 _("You can't Edit this user since it's"
282 " crucial for entire application")
282 " crucial for entire application")
283 )
283 )
284
284
285 for k, v in kwargs.items():
285 for k, v in kwargs.items():
286 if k == 'password' and v:
286 if k == 'password' and v:
287 v = get_crypt_password(v)
287 v = get_crypt_password(v)
288 user.api_key = generate_api_key(user.username)
288 user.api_key = generate_api_key(user.username)
289
289
290 setattr(user, k, v)
290 setattr(user, k, v)
291 self.sa.add(user)
291 self.sa.add(user)
292 return user
292 return user
293 except Exception:
293 except Exception:
294 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
295 raise
295 raise
296
296
297 def delete(self, user):
297 def delete(self, user):
298 user = self._get_user(user)
298 user = self._get_user(user)
299
299
300 try:
300 try:
301 if user.username == 'default':
301 if user.username == 'default':
302 raise DefaultUserException(
302 raise DefaultUserException(
303 _(u"You can't remove this user since it's"
303 _(u"You can't remove this user since it's"
304 " crucial for entire application")
304 " crucial for entire application")
305 )
305 )
306 if user.repositories:
306 if user.repositories:
307 repos = [x.repo_name for x in user.repositories]
307 repos = [x.repo_name for x in user.repositories]
308 raise UserOwnsReposException(
308 raise UserOwnsReposException(
309 _(u'user "%s" still owns %s repositories and cannot be '
309 _(u'user "%s" still owns %s repositories and cannot be '
310 'removed. Switch owners or remove those repositories. %s')
310 'removed. Switch owners or remove those repositories. %s')
311 % (user.username, len(repos), ', '.join(repos))
311 % (user.username, len(repos), ', '.join(repos))
312 )
312 )
313 self.sa.delete(user)
313 self.sa.delete(user)
314 except Exception:
314 except Exception:
315 log.error(traceback.format_exc())
315 log.error(traceback.format_exc())
316 raise
316 raise
317
317
318 def reset_password_link(self, data):
318 def reset_password_link(self, data):
319 from rhodecode.lib.celerylib import tasks, run_task
319 from rhodecode.lib.celerylib import tasks, run_task
320 from rhodecode.model.notification import EmailNotificationModel
320 from rhodecode.model.notification import EmailNotificationModel
321 user_email = data['email']
321 user_email = data['email']
322 try:
322 try:
323 user = User.get_by_email(user_email)
323 user = User.get_by_email(user_email)
324 if user:
324 if user:
325 log.debug('password reset user found %s' % user)
325 log.debug('password reset user found %s' % user)
326 link = url('reset_password_confirmation', key=user.api_key,
326 link = url('reset_password_confirmation', key=user.api_key,
327 qualified=True)
327 qualified=True)
328 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
328 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
329 body = EmailNotificationModel().get_email_tmpl(reg_type,
329 body = EmailNotificationModel().get_email_tmpl(reg_type,
330 **{'user': user.short_contact,
330 **{'user': user.short_contact,
331 'reset_url': link})
331 'reset_url': link})
332 log.debug('sending email')
332 log.debug('sending email')
333 run_task(tasks.send_email, user_email,
333 run_task(tasks.send_email, user_email,
334 _("Password reset link"), body, body)
334 _("Password reset link"), body, body)
335 log.info('send new password mail to %s' % user_email)
335 log.info('send new password mail to %s' % user_email)
336 else:
336 else:
337 log.debug("password reset email %s not found" % user_email)
337 log.debug("password reset email %s not found" % user_email)
338 except Exception:
338 except Exception:
339 log.error(traceback.format_exc())
339 log.error(traceback.format_exc())
340 return False
340 return False
341
341
342 return True
342 return True
343
343
344 def reset_password(self, data):
344 def reset_password(self, data):
345 from rhodecode.lib.celerylib import tasks, run_task
345 from rhodecode.lib.celerylib import tasks, run_task
346 from rhodecode.lib import auth
346 from rhodecode.lib import auth
347 user_email = data['email']
347 user_email = data['email']
348 try:
348 try:
349 try:
349 try:
350 user = User.get_by_email(user_email)
350 user = User.get_by_email(user_email)
351 new_passwd = auth.PasswordGenerator().gen_password(8,
351 new_passwd = auth.PasswordGenerator().gen_password(8,
352 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
352 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
353 if user:
353 if user:
354 user.password = auth.get_crypt_password(new_passwd)
354 user.password = auth.get_crypt_password(new_passwd)
355 user.api_key = auth.generate_api_key(user.username)
355 user.api_key = auth.generate_api_key(user.username)
356 Session().add(user)
356 Session().add(user)
357 Session().commit()
357 Session().commit()
358 log.info('change password for %s' % user_email)
358 log.info('change password for %s' % user_email)
359 if new_passwd is None:
359 if new_passwd is None:
360 raise Exception('unable to generate new password')
360 raise Exception('unable to generate new password')
361 except Exception:
361 except Exception:
362 log.error(traceback.format_exc())
362 log.error(traceback.format_exc())
363 Session().rollback()
363 Session().rollback()
364
364
365 run_task(tasks.send_email, user_email,
365 run_task(tasks.send_email, user_email,
366 _('Your new password'),
366 _('Your new password'),
367 _('Your new RhodeCode password:%s') % (new_passwd))
367 _('Your new RhodeCode password:%s') % (new_passwd))
368 log.info('send new password mail to %s' % user_email)
368 log.info('send new password mail to %s' % user_email)
369
369
370 except Exception:
370 except Exception:
371 log.error('Failed to update user password')
371 log.error('Failed to update user password')
372 log.error(traceback.format_exc())
372 log.error(traceback.format_exc())
373
373
374 return True
374 return True
375
375
376 def fill_data(self, auth_user, user_id=None, api_key=None):
376 def fill_data(self, auth_user, user_id=None, api_key=None):
377 """
377 """
378 Fetches auth_user by user_id,or api_key if present.
378 Fetches auth_user by user_id,or api_key if present.
379 Fills auth_user attributes with those taken from database.
379 Fills auth_user attributes with those taken from database.
380 Additionally set's is_authenitated if lookup fails
380 Additionally set's is_authenitated if lookup fails
381 present in database
381 present in database
382
382
383 :param auth_user: instance of user to set attributes
383 :param auth_user: instance of user to set attributes
384 :param user_id: user id to fetch by
384 :param user_id: user id to fetch by
385 :param api_key: api key to fetch by
385 :param api_key: api key to fetch by
386 """
386 """
387 if user_id is None and api_key is None:
387 if user_id is None and api_key is None:
388 raise Exception('You need to pass user_id or api_key')
388 raise Exception('You need to pass user_id or api_key')
389
389
390 try:
390 try:
391 if api_key:
391 if api_key:
392 dbuser = self.get_by_api_key(api_key)
392 dbuser = self.get_by_api_key(api_key)
393 else:
393 else:
394 dbuser = self.get(user_id)
394 dbuser = self.get(user_id)
395
395
396 if dbuser is not None and dbuser.active:
396 if dbuser is not None and dbuser.active:
397 log.debug('filling %s data' % dbuser)
397 log.debug('filling %s data' % dbuser)
398 for k, v in dbuser.get_dict().items():
398 for k, v in dbuser.get_dict().items():
399 setattr(auth_user, k, v)
399 setattr(auth_user, k, v)
400 else:
400 else:
401 return False
401 return False
402
402
403 except Exception:
403 except Exception:
404 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
405 auth_user.is_authenticated = False
405 auth_user.is_authenticated = False
406 return False
406 return False
407
407
408 return True
408 return True
409
409
410 def fill_perms(self, user, explicit=True, algo='higherwin'):
410 def fill_perms(self, user, explicit=True, algo='higherwin'):
411 """
411 """
412 Fills user permission attribute with permissions taken from database
412 Fills user permission attribute with permissions taken from database
413 works for permissions given for repositories, and for permissions that
413 works for permissions given for repositories, and for permissions that
414 are granted to groups
414 are granted to groups
415
415
416 :param user: user instance to fill his perms
416 :param user: user instance to fill his perms
417 :param explicit: In case there are permissions both for user and a group
417 :param explicit: In case there are permissions both for user and a group
418 that user is part of, explicit flag will defiine if user will
418 that user is part of, explicit flag will defiine if user will
419 explicitly override permissions from group, if it's False it will
419 explicitly override permissions from group, if it's False it will
420 make decision based on the algo
420 make decision based on the algo
421 :param algo: algorithm to decide what permission should be choose if
421 :param algo: algorithm to decide what permission should be choose if
422 it's multiple defined, eg user in two different groups. It also
422 it's multiple defined, eg user in two different groups. It also
423 decides if explicit flag is turned off how to specify the permission
423 decides if explicit flag is turned off how to specify the permission
424 for case when user is in a group + have defined separate permission
424 for case when user is in a group + have defined separate permission
425 """
425 """
426 RK = 'repositories'
426 RK = 'repositories'
427 GK = 'repositories_groups'
427 GK = 'repositories_groups'
428 UK = 'user_groups'
428 UK = 'user_groups'
429 GLOBAL = 'global'
429 GLOBAL = 'global'
430 user.permissions[RK] = {}
430 user.permissions[RK] = {}
431 user.permissions[GK] = {}
431 user.permissions[GK] = {}
432 user.permissions[UK] = {}
432 user.permissions[UK] = {}
433 user.permissions[GLOBAL] = set()
433 user.permissions[GLOBAL] = set()
434
434
435 def _choose_perm(new_perm, cur_perm):
435 def _choose_perm(new_perm, cur_perm):
436 new_perm_val = PERM_WEIGHTS[new_perm]
436 new_perm_val = PERM_WEIGHTS[new_perm]
437 cur_perm_val = PERM_WEIGHTS[cur_perm]
437 cur_perm_val = PERM_WEIGHTS[cur_perm]
438 if algo == 'higherwin':
438 if algo == 'higherwin':
439 if new_perm_val > cur_perm_val:
439 if new_perm_val > cur_perm_val:
440 return new_perm
440 return new_perm
441 return cur_perm
441 return cur_perm
442 elif algo == 'lowerwin':
442 elif algo == 'lowerwin':
443 if new_perm_val < cur_perm_val:
443 if new_perm_val < cur_perm_val:
444 return new_perm
444 return new_perm
445 return cur_perm
445 return cur_perm
446
446
447 #======================================================================
447 #======================================================================
448 # fetch default permissions
448 # fetch default permissions
449 #======================================================================
449 #======================================================================
450 default_user = User.get_by_username('default', cache=True)
450 default_user = User.get_by_username('default', cache=True)
451 default_user_id = default_user.user_id
451 default_user_id = default_user.user_id
452
452
453 default_repo_perms = Permission.get_default_perms(default_user_id)
453 default_repo_perms = Permission.get_default_perms(default_user_id)
454 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
454 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
455 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
455 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
456
456
457 if user.is_admin:
457 if user.is_admin:
458 #==================================================================
458 #==================================================================
459 # admin user have all default rights for repositories
459 # admin user have all default rights for repositories
460 # and groups set to admin
460 # and groups set to admin
461 #==================================================================
461 #==================================================================
462 user.permissions[GLOBAL].add('hg.admin')
462 user.permissions[GLOBAL].add('hg.admin')
463
463
464 # repositories
464 # repositories
465 for perm in default_repo_perms:
465 for perm in default_repo_perms:
466 r_k = perm.UserRepoToPerm.repository.repo_name
466 r_k = perm.UserRepoToPerm.repository.repo_name
467 p = 'repository.admin'
467 p = 'repository.admin'
468 user.permissions[RK][r_k] = p
468 user.permissions[RK][r_k] = p
469
469
470 # repository groups
470 # repository groups
471 for perm in default_repo_groups_perms:
471 for perm in default_repo_groups_perms:
472 rg_k = perm.UserRepoGroupToPerm.group.group_name
472 rg_k = perm.UserRepoGroupToPerm.group.group_name
473 p = 'group.admin'
473 p = 'group.admin'
474 user.permissions[GK][rg_k] = p
474 user.permissions[GK][rg_k] = p
475
475
476 # user groups
476 # user groups
477 for perm in default_user_group_perms:
477 for perm in default_user_group_perms:
478 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
478 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
479 p = 'usergroup.admin'
479 p = 'usergroup.admin'
480 user.permissions[UK][u_k] = p
480 user.permissions[UK][u_k] = p
481 return user
481 return user
482
482
483 #==================================================================
483 #==================================================================
484 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
484 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
485 #==================================================================
485 #==================================================================
486 uid = user.user_id
486 uid = user.user_id
487
487
488 # default global permissions taken fron the default user
488 # default global permissions taken fron the default user
489 default_global_perms = self.sa.query(UserToPerm)\
489 default_global_perms = self.sa.query(UserToPerm)\
490 .filter(UserToPerm.user_id == default_user_id)
490 .filter(UserToPerm.user_id == default_user_id)
491
491
492 for perm in default_global_perms:
492 for perm in default_global_perms:
493 user.permissions[GLOBAL].add(perm.permission.permission_name)
493 user.permissions[GLOBAL].add(perm.permission.permission_name)
494
494
495 # defaults for repositories, taken from default user
495 # defaults for repositories, taken from default user
496 for perm in default_repo_perms:
496 for perm in default_repo_perms:
497 r_k = perm.UserRepoToPerm.repository.repo_name
497 r_k = perm.UserRepoToPerm.repository.repo_name
498 if perm.Repository.private and not (perm.Repository.user_id == uid):
498 if perm.Repository.private and not (perm.Repository.user_id == uid):
499 # disable defaults for private repos,
499 # disable defaults for private repos,
500 p = 'repository.none'
500 p = 'repository.none'
501 elif perm.Repository.user_id == uid:
501 elif perm.Repository.user_id == uid:
502 # set admin if owner
502 # set admin if owner
503 p = 'repository.admin'
503 p = 'repository.admin'
504 else:
504 else:
505 p = perm.Permission.permission_name
505 p = perm.Permission.permission_name
506
506
507 user.permissions[RK][r_k] = p
507 user.permissions[RK][r_k] = p
508
508
509 # defaults for repository groups taken from default user permission
509 # defaults for repository groups taken from default user permission
510 # on given group
510 # on given group
511 for perm in default_repo_groups_perms:
511 for perm in default_repo_groups_perms:
512 rg_k = perm.UserRepoGroupToPerm.group.group_name
512 rg_k = perm.UserRepoGroupToPerm.group.group_name
513 p = perm.Permission.permission_name
513 p = perm.Permission.permission_name
514 user.permissions[GK][rg_k] = p
514 user.permissions[GK][rg_k] = p
515
515
516 # defaults for user groups taken from default user permission
516 # defaults for user groups taken from default user permission
517 # on given user group
517 # on given user group
518 for perm in default_user_group_perms:
518 for perm in default_user_group_perms:
519 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
519 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
520 p = perm.Permission.permission_name
520 p = perm.Permission.permission_name
521 user.permissions[UK][u_k] = p
521 user.permissions[UK][u_k] = p
522
522
523 #======================================================================
523 #======================================================================
524 # !! OVERRIDE GLOBALS !! with user permissions if any found
524 # !! OVERRIDE GLOBALS !! with user permissions if any found
525 #======================================================================
525 #======================================================================
526 # those can be configured from groups or users explicitly
526 # those can be configured from groups or users explicitly
527 _configurable = set([
527 _configurable = set([
528 'hg.fork.none', 'hg.fork.repository',
528 'hg.fork.none', 'hg.fork.repository',
529 'hg.create.none', 'hg.create.repository',
529 'hg.create.none', 'hg.create.repository',
530 'hg.usergroup.create.false', 'hg.usergroup.create.true'
530 'hg.usergroup.create.false', 'hg.usergroup.create.true'
531 ])
531 ])
532
532
533 # USER GROUPS comes first
533 # USER GROUPS comes first
534 # user group global permissions
534 # user group global permissions
535 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
535 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
536 .options(joinedload(UserGroupToPerm.permission))\
536 .options(joinedload(UserGroupToPerm.permission))\
537 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
537 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
538 UserGroupMember.users_group_id))\
538 UserGroupMember.users_group_id))\
539 .filter(UserGroupMember.user_id == uid)\
539 .filter(UserGroupMember.user_id == uid)\
540 .order_by(UserGroupToPerm.users_group_id)\
540 .order_by(UserGroupToPerm.users_group_id)\
541 .all()
541 .all()
542 #need to group here by groups since user can be in more than one group
542 #need to group here by groups since user can be in more than one group
543 _grouped = [[x, list(y)] for x, y in
543 _grouped = [[x, list(y)] for x, y in
544 itertools.groupby(user_perms_from_users_groups,
544 itertools.groupby(user_perms_from_users_groups,
545 lambda x:x.users_group)]
545 lambda x:x.users_group)]
546 for gr, perms in _grouped:
546 for gr, perms in _grouped:
547 # since user can be in multiple groups iterate over them and
547 # since user can be in multiple groups iterate over them and
548 # select the lowest permissions first (more explicit)
548 # select the lowest permissions first (more explicit)
549 ##TODO: do this^^
549 ##TODO: do this^^
550 if not gr.inherit_default_permissions:
550 if not gr.inherit_default_permissions:
551 # NEED TO IGNORE all configurable permissions and
551 # NEED TO IGNORE all configurable permissions and
552 # replace them with explicitly set
552 # replace them with explicitly set
553 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
553 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
554 .difference(_configurable)
554 .difference(_configurable)
555 for perm in perms:
555 for perm in perms:
556 user.permissions[GLOBAL].add(perm.permission.permission_name)
556 user.permissions[GLOBAL].add(perm.permission.permission_name)
557
557
558 # user specific global permissions
558 # user specific global permissions
559 user_perms = self.sa.query(UserToPerm)\
559 user_perms = self.sa.query(UserToPerm)\
560 .options(joinedload(UserToPerm.permission))\
560 .options(joinedload(UserToPerm.permission))\
561 .filter(UserToPerm.user_id == uid).all()
561 .filter(UserToPerm.user_id == uid).all()
562
562
563 if not user.inherit_default_permissions:
563 if not user.inherit_default_permissions:
564 # NEED TO IGNORE all configurable permissions and
564 # NEED TO IGNORE all configurable permissions and
565 # replace them with explicitly set
565 # replace them with explicitly set
566 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
566 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
567 .difference(_configurable)
567 .difference(_configurable)
568
568
569 for perm in user_perms:
569 for perm in user_perms:
570 user.permissions[GLOBAL].add(perm.permission.permission_name)
570 user.permissions[GLOBAL].add(perm.permission.permission_name)
571 ## END GLOBAL PERMISSIONS
571 ## END GLOBAL PERMISSIONS
572
572
573
574 #======================================================================
573 #======================================================================
575 # !! PERMISSIONS FOR REPOSITORIES !!
574 # !! PERMISSIONS FOR REPOSITORIES !!
576 #======================================================================
575 #======================================================================
577 #======================================================================
576 #======================================================================
578 # check if user is part of user groups for this repository and
577 # check if user is part of user groups for this repository and
579 # fill in his permission from it. _choose_perm decides of which
578 # fill in his permission from it. _choose_perm decides of which
580 # permission should be selected based on selected method
579 # permission should be selected based on selected method
581 #======================================================================
580 #======================================================================
582
581
583 # user group for repositories permissions
582 # user group for repositories permissions
584 user_repo_perms_from_users_groups = \
583 user_repo_perms_from_users_groups = \
585 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
584 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
586 .join((Repository, UserGroupRepoToPerm.repository_id ==
585 .join((Repository, UserGroupRepoToPerm.repository_id ==
587 Repository.repo_id))\
586 Repository.repo_id))\
588 .join((Permission, UserGroupRepoToPerm.permission_id ==
587 .join((Permission, UserGroupRepoToPerm.permission_id ==
589 Permission.permission_id))\
588 Permission.permission_id))\
590 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
589 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
591 UserGroupMember.users_group_id))\
590 UserGroupMember.users_group_id))\
592 .filter(UserGroupMember.user_id == uid)\
591 .filter(UserGroupMember.user_id == uid)\
593 .all()
592 .all()
594
593
595 multiple_counter = collections.defaultdict(int)
594 multiple_counter = collections.defaultdict(int)
596 for perm in user_repo_perms_from_users_groups:
595 for perm in user_repo_perms_from_users_groups:
597 r_k = perm.UserGroupRepoToPerm.repository.repo_name
596 r_k = perm.UserGroupRepoToPerm.repository.repo_name
598 multiple_counter[r_k] += 1
597 multiple_counter[r_k] += 1
599 p = perm.Permission.permission_name
598 p = perm.Permission.permission_name
600 cur_perm = user.permissions[RK][r_k]
599 cur_perm = user.permissions[RK][r_k]
601
600
602 if perm.Repository.user_id == uid:
601 if perm.Repository.user_id == uid:
603 # set admin if owner
602 # set admin if owner
604 p = 'repository.admin'
603 p = 'repository.admin'
605 else:
604 else:
606 if multiple_counter[r_k] > 1:
605 if multiple_counter[r_k] > 1:
607 p = _choose_perm(p, cur_perm)
606 p = _choose_perm(p, cur_perm)
608 user.permissions[RK][r_k] = p
607 user.permissions[RK][r_k] = p
609
608
610 # user explicit permissions for repositories, overrides any specified
609 # user explicit permissions for repositories, overrides any specified
611 # by the group permission
610 # by the group permission
612 user_repo_perms = Permission.get_default_perms(uid)
611 user_repo_perms = Permission.get_default_perms(uid)
613 for perm in user_repo_perms:
612 for perm in user_repo_perms:
614 r_k = perm.UserRepoToPerm.repository.repo_name
613 r_k = perm.UserRepoToPerm.repository.repo_name
615 cur_perm = user.permissions[RK][r_k]
614 cur_perm = user.permissions[RK][r_k]
616 # set admin if owner
615 # set admin if owner
617 if perm.Repository.user_id == uid:
616 if perm.Repository.user_id == uid:
618 p = 'repository.admin'
617 p = 'repository.admin'
619 else:
618 else:
620 p = perm.Permission.permission_name
619 p = perm.Permission.permission_name
621 if not explicit:
620 if not explicit:
622 p = _choose_perm(p, cur_perm)
621 p = _choose_perm(p, cur_perm)
623 user.permissions[RK][r_k] = p
622 user.permissions[RK][r_k] = p
624
623
625 #======================================================================
624 #======================================================================
626 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
625 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
627 #======================================================================
626 #======================================================================
628 #======================================================================
627 #======================================================================
629 # check if user is part of user groups for this repository groups and
628 # check if user is part of user groups for this repository groups and
630 # fill in his permission from it. _choose_perm decides of which
629 # fill in his permission from it. _choose_perm decides of which
631 # permission should be selected based on selected method
630 # permission should be selected based on selected method
632 #======================================================================
631 #======================================================================
633 # user group for repo groups permissions
632 # user group for repo groups permissions
634 user_repo_group_perms_from_users_groups = \
633 user_repo_group_perms_from_users_groups = \
635 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
634 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
636 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
635 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
637 .join((Permission, UserGroupRepoGroupToPerm.permission_id
636 .join((Permission, UserGroupRepoGroupToPerm.permission_id
638 == Permission.permission_id))\
637 == Permission.permission_id))\
639 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
638 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
640 == UserGroupMember.users_group_id))\
639 == UserGroupMember.users_group_id))\
641 .filter(UserGroupMember.user_id == uid)\
640 .filter(UserGroupMember.user_id == uid)\
642 .all()
641 .all()
643
642
644 multiple_counter = collections.defaultdict(int)
643 multiple_counter = collections.defaultdict(int)
645 for perm in user_repo_group_perms_from_users_groups:
644 for perm in user_repo_group_perms_from_users_groups:
646 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
645 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
647 multiple_counter[g_k] += 1
646 multiple_counter[g_k] += 1
648 p = perm.Permission.permission_name
647 p = perm.Permission.permission_name
649 cur_perm = user.permissions[GK][g_k]
648 cur_perm = user.permissions[GK][g_k]
650 if multiple_counter[g_k] > 1:
649 if multiple_counter[g_k] > 1:
651 p = _choose_perm(p, cur_perm)
650 p = _choose_perm(p, cur_perm)
652 user.permissions[GK][g_k] = p
651 user.permissions[GK][g_k] = p
653
652
654 # user explicit permissions for repository groups
653 # user explicit permissions for repository groups
655 user_repo_groups_perms = Permission.get_default_group_perms(uid)
654 user_repo_groups_perms = Permission.get_default_group_perms(uid)
656 for perm in user_repo_groups_perms:
655 for perm in user_repo_groups_perms:
657 rg_k = perm.UserRepoGroupToPerm.group.group_name
656 rg_k = perm.UserRepoGroupToPerm.group.group_name
658 p = perm.Permission.permission_name
657 p = perm.Permission.permission_name
659 cur_perm = user.permissions[GK][rg_k]
658 cur_perm = user.permissions[GK][rg_k]
660 if not explicit:
659 if not explicit:
661 p = _choose_perm(p, cur_perm)
660 p = _choose_perm(p, cur_perm)
662 user.permissions[GK][rg_k] = p
661 user.permissions[GK][rg_k] = p
663
662
664 #======================================================================
663 #======================================================================
665 # !! PERMISSIONS FOR USER GROUPS !!
664 # !! PERMISSIONS FOR USER GROUPS !!
666 #======================================================================
665 #======================================================================
666 # user group for user group permissions
667 user_group_user_groups_perms = \
668 self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
669 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
670 == UserGroup.users_group_id))\
671 .join((Permission, UserGroupUserGroupToPerm.permission_id
672 == Permission.permission_id))\
673 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
674 == UserGroupMember.users_group_id))\
675 .filter(UserGroupMember.user_id == uid)\
676 .all()
677
678 multiple_counter = collections.defaultdict(int)
679 for perm in user_group_user_groups_perms:
680 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
681 multiple_counter[g_k] += 1
682 p = perm.Permission.permission_name
683 cur_perm = user.permissions[UK][g_k]
684 if multiple_counter[g_k] > 1:
685 p = _choose_perm(p, cur_perm)
686 user.permissions[UK][g_k] = p
687
667 #user explicit permission for user groups
688 #user explicit permission for user groups
668 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
689 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
669 for perm in user_user_groups_perms:
690 for perm in user_user_groups_perms:
670 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
691 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
671 p = perm.Permission.permission_name
692 p = perm.Permission.permission_name
672 cur_perm = user.permissions[UK][u_k]
693 cur_perm = user.permissions[UK][u_k]
673 if not explicit:
694 if not explicit:
674 p = _choose_perm(p, cur_perm)
695 p = _choose_perm(p, cur_perm)
675 user.permissions[UK][u_k] = p
696 user.permissions[UK][u_k] = p
676
697
677 return user
698 return user
678
699
679 def has_perm(self, user, perm):
700 def has_perm(self, user, perm):
680 perm = self._get_perm(perm)
701 perm = self._get_perm(perm)
681 user = self._get_user(user)
702 user = self._get_user(user)
682
703
683 return UserToPerm.query().filter(UserToPerm.user == user)\
704 return UserToPerm.query().filter(UserToPerm.user == user)\
684 .filter(UserToPerm.permission == perm).scalar() is not None
705 .filter(UserToPerm.permission == perm).scalar() is not None
685
706
686 def grant_perm(self, user, perm):
707 def grant_perm(self, user, perm):
687 """
708 """
688 Grant user global permissions
709 Grant user global permissions
689
710
690 :param user:
711 :param user:
691 :param perm:
712 :param perm:
692 """
713 """
693 user = self._get_user(user)
714 user = self._get_user(user)
694 perm = self._get_perm(perm)
715 perm = self._get_perm(perm)
695 # if this permission is already granted skip it
716 # if this permission is already granted skip it
696 _perm = UserToPerm.query()\
717 _perm = UserToPerm.query()\
697 .filter(UserToPerm.user == user)\
718 .filter(UserToPerm.user == user)\
698 .filter(UserToPerm.permission == perm)\
719 .filter(UserToPerm.permission == perm)\
699 .scalar()
720 .scalar()
700 if _perm:
721 if _perm:
701 return
722 return
702 new = UserToPerm()
723 new = UserToPerm()
703 new.user = user
724 new.user = user
704 new.permission = perm
725 new.permission = perm
705 self.sa.add(new)
726 self.sa.add(new)
706
727
707 def revoke_perm(self, user, perm):
728 def revoke_perm(self, user, perm):
708 """
729 """
709 Revoke users global permissions
730 Revoke users global permissions
710
731
711 :param user:
732 :param user:
712 :param perm:
733 :param perm:
713 """
734 """
714 user = self._get_user(user)
735 user = self._get_user(user)
715 perm = self._get_perm(perm)
736 perm = self._get_perm(perm)
716
737
717 obj = UserToPerm.query()\
738 obj = UserToPerm.query()\
718 .filter(UserToPerm.user == user)\
739 .filter(UserToPerm.user == user)\
719 .filter(UserToPerm.permission == perm)\
740 .filter(UserToPerm.permission == perm)\
720 .scalar()
741 .scalar()
721 if obj:
742 if obj:
722 self.sa.delete(obj)
743 self.sa.delete(obj)
723
744
724 def add_extra_email(self, user, email):
745 def add_extra_email(self, user, email):
725 """
746 """
726 Adds email address to UserEmailMap
747 Adds email address to UserEmailMap
727
748
728 :param user:
749 :param user:
729 :param email:
750 :param email:
730 """
751 """
731 from rhodecode.model import forms
752 from rhodecode.model import forms
732 form = forms.UserExtraEmailForm()()
753 form = forms.UserExtraEmailForm()()
733 data = form.to_python(dict(email=email))
754 data = form.to_python(dict(email=email))
734 user = self._get_user(user)
755 user = self._get_user(user)
735
756
736 obj = UserEmailMap()
757 obj = UserEmailMap()
737 obj.user = user
758 obj.user = user
738 obj.email = data['email']
759 obj.email = data['email']
739 self.sa.add(obj)
760 self.sa.add(obj)
740 return obj
761 return obj
741
762
742 def delete_extra_email(self, user, email_id):
763 def delete_extra_email(self, user, email_id):
743 """
764 """
744 Removes email address from UserEmailMap
765 Removes email address from UserEmailMap
745
766
746 :param user:
767 :param user:
747 :param email_id:
768 :param email_id:
748 """
769 """
749 user = self._get_user(user)
770 user = self._get_user(user)
750 obj = UserEmailMap.query().get(email_id)
771 obj = UserEmailMap.query().get(email_id)
751 if obj:
772 if obj:
752 self.sa.delete(obj)
773 self.sa.delete(obj)
753
774
754 def add_extra_ip(self, user, ip):
775 def add_extra_ip(self, user, ip):
755 """
776 """
756 Adds ip address to UserIpMap
777 Adds ip address to UserIpMap
757
778
758 :param user:
779 :param user:
759 :param ip:
780 :param ip:
760 """
781 """
761 from rhodecode.model import forms
782 from rhodecode.model import forms
762 form = forms.UserExtraIpForm()()
783 form = forms.UserExtraIpForm()()
763 data = form.to_python(dict(ip=ip))
784 data = form.to_python(dict(ip=ip))
764 user = self._get_user(user)
785 user = self._get_user(user)
765
786
766 obj = UserIpMap()
787 obj = UserIpMap()
767 obj.user = user
788 obj.user = user
768 obj.ip_addr = data['ip']
789 obj.ip_addr = data['ip']
769 self.sa.add(obj)
790 self.sa.add(obj)
770 return obj
791 return obj
771
792
772 def delete_extra_ip(self, user, ip_id):
793 def delete_extra_ip(self, user, ip_id):
773 """
794 """
774 Removes ip address from UserIpMap
795 Removes ip address from UserIpMap
775
796
776 :param user:
797 :param user:
777 :param ip_id:
798 :param ip_id:
778 """
799 """
779 user = self._get_user(user)
800 user = self._get_user(user)
780 obj = UserIpMap.query().get(ip_id)
801 obj = UserIpMap.query().get(ip_id)
781 if obj:
802 if obj:
782 self.sa.delete(obj)
803 self.sa.delete(obj)
@@ -1,299 +1,343 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.users_group
3 rhodecode.model.users_group
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 user group model for RhodeCode
6 user group model for RhodeCode
7
7
8 :created_on: Oct 1, 2011
8 :created_on: Oct 1, 2011
9 :author: nvinot
9 :author: nvinot
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.model import BaseModel
30 from rhodecode.model import BaseModel
31 from rhodecode.model.db import UserGroupMember, UserGroup,\
31 from rhodecode.model.db import UserGroupMember, UserGroup,\
32 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm
32 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
33 from rhodecode.lib.exceptions import UserGroupsAssignedException
33 UserGroupUserGroupToPerm
34 from rhodecode.lib.exceptions import UserGroupsAssignedException,\
35 RepoGroupAssignmentError
34
36
35 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
36
38
37
39
38 class UserGroupModel(BaseModel):
40 class UserGroupModel(BaseModel):
39
41
40 cls = UserGroup
42 cls = UserGroup
41
43
42 def _get_user_group(self, users_group):
44 def _get_user_group(self, users_group):
43 return self._get_instance(UserGroup, users_group,
45 return self._get_instance(UserGroup, users_group,
44 callback=UserGroup.get_by_group_name)
46 callback=UserGroup.get_by_group_name)
45
47
46 def _create_default_perms(self, user_group):
48 def _create_default_perms(self, user_group):
47 # create default permission
49 # create default permission
48 default_perm = 'usergroup.read'
50 default_perm = 'usergroup.read'
49 def_user = User.get_default_user()
51 def_user = User.get_default_user()
50 for p in def_user.user_perms:
52 for p in def_user.user_perms:
51 if p.permission.permission_name.startswith('usergroup.'):
53 if p.permission.permission_name.startswith('usergroup.'):
52 default_perm = p.permission.permission_name
54 default_perm = p.permission.permission_name
53 break
55 break
54
56
55 user_group_to_perm = UserUserGroupToPerm()
57 user_group_to_perm = UserUserGroupToPerm()
56 user_group_to_perm.permission = Permission.get_by_key(default_perm)
58 user_group_to_perm.permission = Permission.get_by_key(default_perm)
57
59
58 user_group_to_perm.user_group = user_group
60 user_group_to_perm.user_group = user_group
59 user_group_to_perm.user_id = def_user.user_id
61 user_group_to_perm.user_id = def_user.user_id
60 return user_group_to_perm
62 return user_group_to_perm
61
63
62 def _update_permissions(self, user_group, perms_new=None,
64 def _update_permissions(self, user_group, perms_new=None,
63 perms_updates=None):
65 perms_updates=None):
64 if not perms_new:
66 if not perms_new:
65 perms_new = []
67 perms_new = []
66 if not perms_updates:
68 if not perms_updates:
67 perms_updates = []
69 perms_updates = []
68
70
69 # update permissions
71 # update permissions
70 for member, perm, member_type in perms_updates:
72 for member, perm, member_type in perms_updates:
71 if member_type == 'user':
73 if member_type == 'user':
72 # this updates existing one
74 # this updates existing one
73 self.grant_user_permission(
75 self.grant_user_permission(
74 user_group=user_group, user=member, perm=perm
76 user_group=user_group, user=member, perm=perm
75 )
77 )
76 else:
78 else:
77 self.grant_users_group_permission(
79 self.grant_users_group_permission(
78 user_group=user_group, group_name=member, perm=perm
80 target_user_group=user_group, user_group=member, perm=perm
79 )
81 )
80 # set new permissions
82 # set new permissions
81 for member, perm, member_type in perms_new:
83 for member, perm, member_type in perms_new:
82 if member_type == 'user':
84 if member_type == 'user':
83 self.grant_user_permission(
85 self.grant_user_permission(
84 user_group=user_group, user=member, perm=perm
86 user_group=user_group, user=member, perm=perm
85 )
87 )
86 else:
88 else:
87 self.grant_users_group_permission(
89 self.grant_users_group_permission(
88 user_group=user_group, group_name=member, perm=perm
90 target_user_group=user_group, user_group=member, perm=perm
89 )
91 )
90
92
91 def get(self, users_group_id, cache=False):
93 def get(self, users_group_id, cache=False):
92 return UserGroup.get(users_group_id)
94 return UserGroup.get(users_group_id)
93
95
94 def get_group(self, users_group):
96 def get_group(self, users_group):
95 return self._get_user_group(users_group)
97 return self._get_user_group(users_group)
96
98
97 def get_by_name(self, name, cache=False, case_insensitive=False):
99 def get_by_name(self, name, cache=False, case_insensitive=False):
98 return UserGroup.get_by_group_name(name, cache, case_insensitive)
100 return UserGroup.get_by_group_name(name, cache, case_insensitive)
99
101
100 def create(self, name, owner, active=True):
102 def create(self, name, owner, active=True):
101 try:
103 try:
102 new_user_group = UserGroup()
104 new_user_group = UserGroup()
103 new_user_group.user = self._get_user(owner)
105 new_user_group.user = self._get_user(owner)
104 new_user_group.users_group_name = name
106 new_user_group.users_group_name = name
105 new_user_group.users_group_active = active
107 new_user_group.users_group_active = active
106 self.sa.add(new_user_group)
108 self.sa.add(new_user_group)
107 perm_obj = self._create_default_perms(new_user_group)
109 perm_obj = self._create_default_perms(new_user_group)
108 self.sa.add(perm_obj)
110 self.sa.add(perm_obj)
109
111
110 self.grant_user_permission(user_group=new_user_group,
112 self.grant_user_permission(user_group=new_user_group,
111 user=owner, perm='usergroup.admin')
113 user=owner, perm='usergroup.admin')
112
114
113 return new_user_group
115 return new_user_group
114 except Exception:
116 except Exception:
115 log.error(traceback.format_exc())
117 log.error(traceback.format_exc())
116 raise
118 raise
117
119
118 def update(self, users_group, form_data):
120 def update(self, users_group, form_data):
119
121
120 try:
122 try:
121 users_group = self._get_user_group(users_group)
123 users_group = self._get_user_group(users_group)
122
124
123 for k, v in form_data.items():
125 for k, v in form_data.items():
124 if k == 'users_group_members':
126 if k == 'users_group_members':
125 users_group.members = []
127 users_group.members = []
126 self.sa.flush()
128 self.sa.flush()
127 members_list = []
129 members_list = []
128 if v:
130 if v:
129 v = [v] if isinstance(v, basestring) else v
131 v = [v] if isinstance(v, basestring) else v
130 for u_id in set(v):
132 for u_id in set(v):
131 member = UserGroupMember(users_group.users_group_id, u_id)
133 member = UserGroupMember(users_group.users_group_id, u_id)
132 members_list.append(member)
134 members_list.append(member)
133 setattr(users_group, 'members', members_list)
135 setattr(users_group, 'members', members_list)
134 setattr(users_group, k, v)
136 setattr(users_group, k, v)
135
137
136 self.sa.add(users_group)
138 self.sa.add(users_group)
137 except Exception:
139 except Exception:
138 log.error(traceback.format_exc())
140 log.error(traceback.format_exc())
139 raise
141 raise
140
142
141 def delete(self, users_group, force=False):
143 def delete(self, users_group, force=False):
142 """
144 """
143 Deletes repository group, unless force flag is used
145 Deletes repository group, unless force flag is used
144 raises exception if there are members in that group, else deletes
146 raises exception if there are members in that group, else deletes
145 group and users
147 group and users
146
148
147 :param users_group:
149 :param users_group:
148 :param force:
150 :param force:
149 """
151 """
150 try:
152 try:
151 users_group = self._get_user_group(users_group)
153 users_group = self._get_user_group(users_group)
152
154
153 # check if this group is not assigned to repo
155 # check if this group is not assigned to repo
154 assigned_groups = UserGroupRepoToPerm.query()\
156 assigned_groups = UserGroupRepoToPerm.query()\
155 .filter(UserGroupRepoToPerm.users_group == users_group).all()
157 .filter(UserGroupRepoToPerm.users_group == users_group).all()
156
158
157 if assigned_groups and not force:
159 if assigned_groups and not force:
158 raise UserGroupsAssignedException('RepoGroup assigned to %s' %
160 raise UserGroupsAssignedException('RepoGroup assigned to %s' %
159 assigned_groups)
161 assigned_groups)
160
162
161 self.sa.delete(users_group)
163 self.sa.delete(users_group)
162 except Exception:
164 except Exception:
163 log.error(traceback.format_exc())
165 log.error(traceback.format_exc())
164 raise
166 raise
165
167
166 def add_user_to_group(self, users_group, user):
168 def add_user_to_group(self, users_group, user):
167 users_group = self._get_user_group(users_group)
169 users_group = self._get_user_group(users_group)
168 user = self._get_user(user)
170 user = self._get_user(user)
169
171
170 for m in users_group.members:
172 for m in users_group.members:
171 u = m.user
173 u = m.user
172 if u.user_id == user.user_id:
174 if u.user_id == user.user_id:
173 return True
175 return True
174
176
175 try:
177 try:
176 users_group_member = UserGroupMember()
178 users_group_member = UserGroupMember()
177 users_group_member.user = user
179 users_group_member.user = user
178 users_group_member.users_group = users_group
180 users_group_member.users_group = users_group
179
181
180 users_group.members.append(users_group_member)
182 users_group.members.append(users_group_member)
181 user.group_member.append(users_group_member)
183 user.group_member.append(users_group_member)
182
184
183 self.sa.add(users_group_member)
185 self.sa.add(users_group_member)
184 return users_group_member
186 return users_group_member
185 except Exception:
187 except Exception:
186 log.error(traceback.format_exc())
188 log.error(traceback.format_exc())
187 raise
189 raise
188
190
189 def remove_user_from_group(self, users_group, user):
191 def remove_user_from_group(self, users_group, user):
190 users_group = self._get_user_group(users_group)
192 users_group = self._get_user_group(users_group)
191 user = self._get_user(user)
193 user = self._get_user(user)
192
194
193 users_group_member = None
195 users_group_member = None
194 for m in users_group.members:
196 for m in users_group.members:
195 if m.user.user_id == user.user_id:
197 if m.user.user_id == user.user_id:
196 # Found this user's membership row
198 # Found this user's membership row
197 users_group_member = m
199 users_group_member = m
198 break
200 break
199
201
200 if users_group_member:
202 if users_group_member:
201 try:
203 try:
202 self.sa.delete(users_group_member)
204 self.sa.delete(users_group_member)
203 return True
205 return True
204 except Exception:
206 except Exception:
205 log.error(traceback.format_exc())
207 log.error(traceback.format_exc())
206 raise
208 raise
207 else:
209 else:
208 # User isn't in that group
210 # User isn't in that group
209 return False
211 return False
210
212
211 def has_perm(self, users_group, perm):
213 def has_perm(self, users_group, perm):
212 users_group = self._get_user_group(users_group)
214 users_group = self._get_user_group(users_group)
213 perm = self._get_perm(perm)
215 perm = self._get_perm(perm)
214
216
215 return UserGroupToPerm.query()\
217 return UserGroupToPerm.query()\
216 .filter(UserGroupToPerm.users_group == users_group)\
218 .filter(UserGroupToPerm.users_group == users_group)\
217 .filter(UserGroupToPerm.permission == perm).scalar() is not None
219 .filter(UserGroupToPerm.permission == perm).scalar() is not None
218
220
219 def grant_perm(self, users_group, perm):
221 def grant_perm(self, users_group, perm):
220 users_group = self._get_user_group(users_group)
222 users_group = self._get_user_group(users_group)
221 perm = self._get_perm(perm)
223 perm = self._get_perm(perm)
222
224
223 # if this permission is already granted skip it
225 # if this permission is already granted skip it
224 _perm = UserGroupToPerm.query()\
226 _perm = UserGroupToPerm.query()\
225 .filter(UserGroupToPerm.users_group == users_group)\
227 .filter(UserGroupToPerm.users_group == users_group)\
226 .filter(UserGroupToPerm.permission == perm)\
228 .filter(UserGroupToPerm.permission == perm)\
227 .scalar()
229 .scalar()
228 if _perm:
230 if _perm:
229 return
231 return
230
232
231 new = UserGroupToPerm()
233 new = UserGroupToPerm()
232 new.users_group = users_group
234 new.users_group = users_group
233 new.permission = perm
235 new.permission = perm
234 self.sa.add(new)
236 self.sa.add(new)
235
237
236 def revoke_perm(self, users_group, perm):
238 def revoke_perm(self, users_group, perm):
237 users_group = self._get_user_group(users_group)
239 users_group = self._get_user_group(users_group)
238 perm = self._get_perm(perm)
240 perm = self._get_perm(perm)
239
241
240 obj = UserGroupToPerm.query()\
242 obj = UserGroupToPerm.query()\
241 .filter(UserGroupToPerm.users_group == users_group)\
243 .filter(UserGroupToPerm.users_group == users_group)\
242 .filter(UserGroupToPerm.permission == perm).scalar()
244 .filter(UserGroupToPerm.permission == perm).scalar()
243 if obj:
245 if obj:
244 self.sa.delete(obj)
246 self.sa.delete(obj)
245
247
246 def grant_user_permission(self, user_group, user, perm):
248 def grant_user_permission(self, user_group, user, perm):
247 """
249 """
248 Grant permission for user on given user group, or update
250 Grant permission for user on given user group, or update
249 existing one if found
251 existing one if found
250
252
251 :param user_group: Instance of UserGroup, users_group_id,
253 :param user_group: Instance of UserGroup, users_group_id,
252 or users_group_name
254 or users_group_name
253 :param user: Instance of User, user_id or username
255 :param user: Instance of User, user_id or username
254 :param perm: Instance of Permission, or permission_name
256 :param perm: Instance of Permission, or permission_name
255 """
257 """
256
258
257 user_group = self._get_user_group(user_group)
259 user_group = self._get_user_group(user_group)
258 user = self._get_user(user)
260 user = self._get_user(user)
259 permission = self._get_perm(perm)
261 permission = self._get_perm(perm)
260
262
261 # check if we have that permission already
263 # check if we have that permission already
262 obj = self.sa.query(UserUserGroupToPerm)\
264 obj = self.sa.query(UserUserGroupToPerm)\
263 .filter(UserUserGroupToPerm.user == user)\
265 .filter(UserUserGroupToPerm.user == user)\
264 .filter(UserUserGroupToPerm.user_group == user_group)\
266 .filter(UserUserGroupToPerm.user_group == user_group)\
265 .scalar()
267 .scalar()
266 if obj is None:
268 if obj is None:
267 # create new !
269 # create new !
268 obj = UserUserGroupToPerm()
270 obj = UserUserGroupToPerm()
269 obj.user_group = user_group
271 obj.user_group = user_group
270 obj.user = user
272 obj.user = user
271 obj.permission = permission
273 obj.permission = permission
272 self.sa.add(obj)
274 self.sa.add(obj)
273 log.debug('Granted perm %s to %s on %s' % (perm, user, user_group))
275 log.debug('Granted perm %s to %s on %s' % (perm, user, user_group))
274
276
275 def revoke_user_permission(self, user_group, user):
277 def revoke_user_permission(self, user_group, user):
276 """
278 """
277 Revoke permission for user on given repository group
279 Revoke permission for user on given repository group
278
280
279 :param user_group: Instance of ReposGroup, repositories_group_id,
281 :param user_group: Instance of ReposGroup, repositories_group_id,
280 or repositories_group name
282 or repositories_group name
281 :param user: Instance of User, user_id or username
283 :param user: Instance of User, user_id or username
282 """
284 """
283
285
284 user_group = self._get_user_group(user_group)
286 user_group = self._get_user_group(user_group)
285 user = self._get_user(user)
287 user = self._get_user(user)
286
288
287 obj = self.sa.query(UserUserGroupToPerm)\
289 obj = self.sa.query(UserUserGroupToPerm)\
288 .filter(UserUserGroupToPerm.user == user)\
290 .filter(UserUserGroupToPerm.user == user)\
289 .filter(UserUserGroupToPerm.user_group == user_group)\
291 .filter(UserUserGroupToPerm.user_group == user_group)\
290 .scalar()
292 .scalar()
291 if obj:
293 if obj:
292 self.sa.delete(obj)
294 self.sa.delete(obj)
293 log.debug('Revoked perm on %s on %s' % (user_group, user))
295 log.debug('Revoked perm on %s on %s' % (user_group, user))
294
296
295 def grant_users_group_permission(self, user_group, group_name, perm):
297 def grant_users_group_permission(self, target_user_group, user_group, perm):
296 raise NotImplementedError()
298 """
299 Grant user group permission for given target_user_group
300
301 :param target_user_group:
302 :param user_group:
303 :param perm:
304 """
305 target_user_group = self._get_user_group(target_user_group)
306 user_group = self._get_user_group(user_group)
307 permission = self._get_perm(perm)
308 # forbid assigning same user group to itself
309 if target_user_group == user_group:
310 raise RepoGroupAssignmentError('target repo:%s cannot be '
311 'assigned to itself' % target_user_group)
297
312
298 def revoke_users_group_permission(self, user_group, group_name):
313 # check if we have that permission already
299 raise NotImplementedError()
314 obj = self.sa.query(UserGroupUserGroupToPerm)\
315 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
316 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
317 .scalar()
318 if obj is None:
319 # create new !
320 obj = UserGroupUserGroupToPerm()
321 obj.user_group = user_group
322 obj.target_user_group = target_user_group
323 obj.permission = permission
324 self.sa.add(obj)
325 log.debug('Granted perm %s to %s on %s' % (perm, target_user_group, user_group))
326
327 def revoke_users_group_permission(self, target_user_group, user_group):
328 """
329 Revoke user group permission for given target_user_group
330
331 :param target_user_group:
332 :param user_group:
333 """
334 target_user_group = self._get_user_group(target_user_group)
335 user_group = self._get_user_group(user_group)
336
337 obj = self.sa.query(UserGroupUserGroupToPerm)\
338 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
339 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
340 .scalar()
341 if obj:
342 self.sa.delete(obj)
343 log.debug('Revoked perm on %s on %s' % (target_user_group, user_group))
@@ -1,106 +1,107 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repos_group.repo_group_to_perm:
11 %for r2p in c.repos_group.repo_group_to_perm:
12 ##forbid revoking permission from yourself
12 ##forbid revoking permission from yourself
13 <tr id="id${id(r2p.user.username)}">
13 <tr id="id${id(r2p.user.username)}">
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
19 <td style="white-space: nowrap;">
19 <td style="white-space: nowrap;">
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
21 </td>
21 </td>
22 <td>
22 <td>
23 %if r2p.user.username !='default':
23 %if r2p.user.username !='default':
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
25 ${_('revoke')}
25 ${_('revoke')}
26 </span>
26 </span>
27 %endif
27 %endif
28 </td>
28 </td>
29 %else:
29 %else:
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
34 <td style="white-space: nowrap;">
34 <td style="white-space: nowrap;">
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
36 </td>
36 </td>
37 <td>
37 <td>
38 </td>
38 </td>
39 %endif
39 %endif
40 </tr>
40 </tr>
41 %endfor
41 %endfor
42
42
43 ## USER GROUPS
43 ## USER GROUPS
44 %for g2p in c.repos_group.users_group_to_perm:
44 %for g2p in c.repos_group.users_group_to_perm:
45 <tr id="id${id(g2p.users_group.users_group_name)}">
45 <tr id="id${id(g2p.users_group.users_group_name)}">
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
48 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
48 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
49 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
49 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
50 <td style="white-space: nowrap;">
50 <td style="white-space: nowrap;">
51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
52 </td>
52 </td>
53 <td>
53 <td>
54 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
54 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
55 ${_('revoke')}
55 ${_('revoke')}
56 </span>
56 </span>
57 </td>
57 </td>
58 </tr>
58 </tr>
59 %endfor
59 %endfor
60 <%
60
61 <%
61 _tmpl = h.literal("""' \
62 _tmpl = h.literal("""' \
62 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
63 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
63 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
64 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
64 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td class="ac"> \
67 <td class="ac"> \
67 <div class="perm_ac" id="perm_ac_{0}"> \
68 <div class="perm_ac" id="perm_ac_{0}"> \
68 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
69 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
69 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
70 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
70 <div id="perm_container_{0}"></div> \
71 <div id="perm_container_{0}"></div> \
71 </div> \
72 </div> \
72 </td> \
73 </td> \
73 <td></td>'""")
74 <td></td>'""")
74 %>
75 %>
75 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
76 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
76 <tr class="new_members last_new_member" id="add_perm_input"></tr>
77 <tr class="new_members last_new_member" id="add_perm_input"></tr>
77 <tr>
78 <tr>
78 <td colspan="6">
79 <td colspan="6">
79 <span id="add_perm" class="add_icon" style="cursor: pointer;">
80 <span id="add_perm" class="add_icon" style="cursor: pointer;">
80 ${_('Add another member')}
81 ${_('Add another member')}
81 </span>
82 </span>
82 </td>
83 </td>
83 </tr>
84 </tr>
84 <tr>
85 <tr>
85 <td colspan="6">
86 <td colspan="6">
86 ${h.checkbox('recursive',value="True", label=_('apply to children'))}
87 ${h.checkbox('recursive',value="True", label=_('apply to children'))}
87 <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
88 <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
88 </td>
89 </td>
89 </tr>
90 </tr>
90 </table>
91 </table>
91 <script type="text/javascript">
92 <script type="text/javascript">
92 function ajaxActionRevoke(obj_id, obj_type, field_id) {
93 function ajaxActionRevoke(obj_id, obj_type, field_id) {
93 url = "${h.url('delete_repo_group_perm_member', group_name=c.repos_group.group_name)}";
94 url = "${h.url('delete_repo_group_perm_member', group_name=c.repos_group.group_name)}";
94 ajaxActionRevokePermission(url, obj_id, obj_type, field_id, {recursive:YUD.get('recursive').checked});
95 ajaxActionRevokePermission(url, obj_id, obj_type, field_id, {recursive:YUD.get('recursive').checked});
95 };
96 };
96
97
97 YUE.onDOMReady(function () {
98 YUE.onDOMReady(function () {
98 if (!YUD.hasClass('perm_new_member_name', 'error')) {
99 if (!YUD.hasClass('perm_new_member_name', 'error')) {
99 YUD.setStyle('add_perm_input', 'display', 'none');
100 YUD.setStyle('add_perm_input', 'display', 'none');
100 }
101 }
101 YAHOO.util.Event.addListener('add_perm', 'click', function () {
102 YAHOO.util.Event.addListener('add_perm', 'click', function () {
102 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
103 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
103 });
104 });
104 });
105 });
105
106
106 </script>
107 </script>
@@ -1,83 +1,101 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.users_group.user_user_group_to_perm:
11 %for r2p in c.users_group.user_user_group_to_perm:
12 ##forbid revoking permission from yourself
12 ##forbid revoking permission from yourself
13 <tr id="id${id(r2p.user.username)}">
13 <tr id="id${id(r2p.user.username)}">
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
19 <td style="white-space: nowrap;">
19 <td style="white-space: nowrap;">
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
21 </td>
21 </td>
22 <td>
22 <td>
23 %if r2p.user.username !='default':
23 %if r2p.user.username !='default':
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
25 ${_('revoke')}
25 ${_('revoke')}
26 </span>
26 </span>
27 %endif
27 %endif
28 </td>
28 </td>
29 %else:
29 %else:
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
34 <td style="white-space: nowrap;">
34 <td style="white-space: nowrap;">
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
36 </td>
36 </td>
37 <td>
37 <td>
38 </td>
38 </td>
39 %endif
39 %endif
40 </tr>
40 </tr>
41 %endfor
41 %endfor
42
42
43 ## USER GROUPS
44 %for g2p in c.users_group.user_group_user_group_to_perm:
45 <tr id="id${id(g2p.user_group.users_group_name)}">
46 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.none')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.read')}</td>
48 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.write')}</td>
49 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.admin')}</td>
50 <td style="white-space: nowrap;">
51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.user_group.users_group_name}
52 </td>
53 <td>
54 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.user_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.user_group.users_group_name)}')">
55 ${_('revoke')}
56 </span>
57 </td>
58 </tr>
59 %endfor
60
43 <%
61 <%
44 _tmpl = h.literal("""' \
62 _tmpl = h.literal("""' \
45 <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
63 <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
46 <td><input type="radio" value="usergroup.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
64 <td><input type="radio" value="usergroup.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
47 <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
48 <td><input type="radio" value="usergroup.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="usergroup.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
49 <td class="ac"> \
67 <td class="ac"> \
50 <div class="perm_ac" id="perm_ac_{0}"> \
68 <div class="perm_ac" id="perm_ac_{0}"> \
51 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
69 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
52 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
70 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
53 <div id="perm_container_{0}"></div> \
71 <div id="perm_container_{0}"></div> \
54 </div> \
72 </div> \
55 </td> \
73 </td> \
56 <td></td>'""")
74 <td></td>'""")
57 %>
75 %>
58 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
76 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
59 <tr class="new_members last_new_member" id="add_perm_input"></tr>
77 <tr class="new_members last_new_member" id="add_perm_input"></tr>
60 <tr>
78 <tr>
61 <td colspan="6">
79 <td colspan="6">
62 <span id="add_perm" class="add_icon" style="cursor: pointer;">
80 <span id="add_perm" class="add_icon" style="cursor: pointer;">
63 ${_('Add another member')}
81 ${_('Add another member')}
64 </span>
82 </span>
65 </td>
83 </td>
66 </tr>
84 </tr>
67 </table>
85 </table>
68 <script type="text/javascript">
86 <script type="text/javascript">
69 function ajaxActionRevoke(obj_id, obj_type, field_id) {
87 function ajaxActionRevoke(obj_id, obj_type, field_id) {
70 url = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
88 url = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
71 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
89 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
72 };
90 };
73
91
74 YUE.onDOMReady(function () {
92 YUE.onDOMReady(function () {
75 if (!YUD.hasClass('perm_new_member_name', 'error')) {
93 if (!YUD.hasClass('perm_new_member_name', 'error')) {
76 YUD.setStyle('add_perm_input', 'display', 'none');
94 YUD.setStyle('add_perm_input', 'display', 'none');
77 }
95 }
78 YAHOO.util.Event.addListener('add_perm', 'click', function () {
96 YAHOO.util.Event.addListener('add_perm', 'click', function () {
79 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
97 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
80 });
98 });
81 });
99 });
82
100
83 </script>
101 </script>
General Comments 0
You need to be logged in to leave comments. Login now