##// END OF EJS Templates
user-groups: moved get_user_groups from RepoModel to UserGroupModel.
marcink -
r1676:40da1231 default
parent child Browse files
Show More
@@ -1,306 +1,307 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from sqlalchemy.sql.functions import coalesce
26 from sqlalchemy.sql.functions import coalesce
27
27
28 from rhodecode.lib.helpers import Page
28 from rhodecode.lib.helpers import Page
29 from rhodecode_tools.lib.ext_json import json
29 from rhodecode_tools.lib.ext_json import json
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import PartialRenderer
35 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 from rhodecode.model.auth_token import AuthTokenModel
37 from rhodecode.model.auth_token import AuthTokenModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.db import User, or_
40 from rhodecode.model.db import User, or_
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class AdminUsersView(BaseAppView, DataGridAppView):
46 class AdminUsersView(BaseAppView, DataGridAppView):
47 ALLOW_SCOPED_TOKENS = False
47 ALLOW_SCOPED_TOKENS = False
48 """
48 """
49 This view has alternative version inside EE, if modified please take a look
49 This view has alternative version inside EE, if modified please take a look
50 in there as well.
50 in there as well.
51 """
51 """
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 self._register_global_c(c)
56 self._register_global_c(c)
57 return c
57 return c
58
58
59 def _redirect_for_default_user(self, username):
59 def _redirect_for_default_user(self, username):
60 _ = self.request.translate
60 _ = self.request.translate
61 if username == User.DEFAULT_USER:
61 if username == User.DEFAULT_USER:
62 h.flash(_("You can't edit this user"), category='warning')
62 h.flash(_("You can't edit this user"), category='warning')
63 # TODO(marcink): redirect to 'users' admin panel once this
63 # TODO(marcink): redirect to 'users' admin panel once this
64 # is a pyramid view
64 # is a pyramid view
65 raise HTTPFound('/')
65 raise HTTPFound('/')
66
66
67 @HasPermissionAllDecorator('hg.admin')
67 @HasPermissionAllDecorator('hg.admin')
68 @view_config(
68 @view_config(
69 route_name='users', request_method='GET',
69 route_name='users', request_method='GET',
70 renderer='rhodecode:templates/admin/users/users.mako')
70 renderer='rhodecode:templates/admin/users/users.mako')
71 def users_list(self):
71 def users_list(self):
72 c = self.load_default_context()
72 c = self.load_default_context()
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
74
75 @HasPermissionAllDecorator('hg.admin')
75 @HasPermissionAllDecorator('hg.admin')
76 @view_config(
76 @view_config(
77 # renderer defined below
77 # renderer defined below
78 route_name='users_data', request_method='GET',
78 route_name='users_data', request_method='GET',
79 renderer='json_ext', xhr=True)
79 renderer='json_ext', xhr=True)
80 def users_list_data(self):
80 def users_list_data(self):
81 draw, start, limit = self._extract_chunk(self.request)
81 draw, start, limit = self._extract_chunk(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
83
83
84 _render = PartialRenderer('data_table/_dt_elements.mako')
84 _render = PartialRenderer('data_table/_dt_elements.mako')
85
85
86 def user_actions(user_id, username):
86 def user_actions(user_id, username):
87 return _render("user_actions", user_id, username)
87 return _render("user_actions", user_id, username)
88
88
89 users_data_total_count = User.query()\
89 users_data_total_count = User.query()\
90 .filter(User.username != User.DEFAULT_USER) \
90 .filter(User.username != User.DEFAULT_USER) \
91 .count()
91 .count()
92
92
93 # json generate
93 # json generate
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
95
95
96 if search_q:
96 if search_q:
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
98 base_q = base_q.filter(or_(
98 base_q = base_q.filter(or_(
99 User.username.ilike(like_expression),
99 User.username.ilike(like_expression),
100 User._email.ilike(like_expression),
100 User._email.ilike(like_expression),
101 User.name.ilike(like_expression),
101 User.name.ilike(like_expression),
102 User.lastname.ilike(like_expression),
102 User.lastname.ilike(like_expression),
103 ))
103 ))
104
104
105 users_data_total_filtered_count = base_q.count()
105 users_data_total_filtered_count = base_q.count()
106
106
107 sort_col = getattr(User, order_by, None)
107 sort_col = getattr(User, order_by, None)
108 if sort_col:
108 if sort_col:
109 if order_dir == 'asc':
109 if order_dir == 'asc':
110 # handle null values properly to order by NULL last
110 # handle null values properly to order by NULL last
111 if order_by in ['last_activity']:
111 if order_by in ['last_activity']:
112 sort_col = coalesce(sort_col, datetime.date.max)
112 sort_col = coalesce(sort_col, datetime.date.max)
113 sort_col = sort_col.asc()
113 sort_col = sort_col.asc()
114 else:
114 else:
115 # handle null values properly to order by NULL last
115 # handle null values properly to order by NULL last
116 if order_by in ['last_activity']:
116 if order_by in ['last_activity']:
117 sort_col = coalesce(sort_col, datetime.date.min)
117 sort_col = coalesce(sort_col, datetime.date.min)
118 sort_col = sort_col.desc()
118 sort_col = sort_col.desc()
119
119
120 base_q = base_q.order_by(sort_col)
120 base_q = base_q.order_by(sort_col)
121 base_q = base_q.offset(start).limit(limit)
121 base_q = base_q.offset(start).limit(limit)
122
122
123 users_list = base_q.all()
123 users_list = base_q.all()
124
124
125 users_data = []
125 users_data = []
126 for user in users_list:
126 for user in users_list:
127 users_data.append({
127 users_data.append({
128 "username": h.gravatar_with_user(user.username),
128 "username": h.gravatar_with_user(user.username),
129 "email": user.email,
129 "email": user.email,
130 "first_name": h.escape(user.name),
130 "first_name": h.escape(user.name),
131 "last_name": h.escape(user.lastname),
131 "last_name": h.escape(user.lastname),
132 "last_login": h.format_date(user.last_login),
132 "last_login": h.format_date(user.last_login),
133 "last_activity": h.format_date(user.last_activity),
133 "last_activity": h.format_date(user.last_activity),
134 "active": h.bool2icon(user.active),
134 "active": h.bool2icon(user.active),
135 "active_raw": user.active,
135 "active_raw": user.active,
136 "admin": h.bool2icon(user.admin),
136 "admin": h.bool2icon(user.admin),
137 "extern_type": user.extern_type,
137 "extern_type": user.extern_type,
138 "extern_name": user.extern_name,
138 "extern_name": user.extern_name,
139 "action": user_actions(user.user_id, user.username),
139 "action": user_actions(user.user_id, user.username),
140 })
140 })
141
141
142 data = ({
142 data = ({
143 'draw': draw,
143 'draw': draw,
144 'data': users_data,
144 'data': users_data,
145 'recordsTotal': users_data_total_count,
145 'recordsTotal': users_data_total_count,
146 'recordsFiltered': users_data_total_filtered_count,
146 'recordsFiltered': users_data_total_filtered_count,
147 })
147 })
148
148
149 return data
149 return data
150
150
151 @LoginRequired()
151 @LoginRequired()
152 @HasPermissionAllDecorator('hg.admin')
152 @HasPermissionAllDecorator('hg.admin')
153 @view_config(
153 @view_config(
154 route_name='edit_user_auth_tokens', request_method='GET',
154 route_name='edit_user_auth_tokens', request_method='GET',
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
156 def auth_tokens(self):
156 def auth_tokens(self):
157 _ = self.request.translate
157 _ = self.request.translate
158 c = self.load_default_context()
158 c = self.load_default_context()
159
159
160 user_id = self.request.matchdict.get('user_id')
160 user_id = self.request.matchdict.get('user_id')
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
162 self._redirect_for_default_user(c.user.username)
162 self._redirect_for_default_user(c.user.username)
163
163
164 c.active = 'auth_tokens'
164 c.active = 'auth_tokens'
165
165
166 c.lifetime_values = [
166 c.lifetime_values = [
167 (str(-1), _('forever')),
167 (str(-1), _('forever')),
168 (str(5), _('5 minutes')),
168 (str(5), _('5 minutes')),
169 (str(60), _('1 hour')),
169 (str(60), _('1 hour')),
170 (str(60 * 24), _('1 day')),
170 (str(60 * 24), _('1 day')),
171 (str(60 * 24 * 30), _('1 month')),
171 (str(60 * 24 * 30), _('1 month')),
172 ]
172 ]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
174 c.role_values = [
174 c.role_values = [
175 (x, AuthTokenModel.cls._get_role_name(x))
175 (x, AuthTokenModel.cls._get_role_name(x))
176 for x in AuthTokenModel.cls.ROLES]
176 for x in AuthTokenModel.cls.ROLES]
177 c.role_options = [(c.role_values, _("Role"))]
177 c.role_options = [(c.role_values, _("Role"))]
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
179 c.user.user_id, show_expired=True)
179 c.user.user_id, show_expired=True)
180 return self._get_template_context(c)
180 return self._get_template_context(c)
181
181
182 def maybe_attach_token_scope(self, token):
182 def maybe_attach_token_scope(self, token):
183 # implemented in EE edition
183 # implemented in EE edition
184 pass
184 pass
185
185
186 @LoginRequired()
186 @LoginRequired()
187 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
188 @CSRFRequired()
188 @CSRFRequired()
189 @view_config(
189 @view_config(
190 route_name='edit_user_auth_tokens_add', request_method='POST')
190 route_name='edit_user_auth_tokens_add', request_method='POST')
191 def auth_tokens_add(self):
191 def auth_tokens_add(self):
192 _ = self.request.translate
192 _ = self.request.translate
193 c = self.load_default_context()
193 c = self.load_default_context()
194
194
195 user_id = self.request.matchdict.get('user_id')
195 user_id = self.request.matchdict.get('user_id')
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
197 self._redirect_for_default_user(c.user.username)
197 self._redirect_for_default_user(c.user.username)
198
198
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
200 description = self.request.POST.get('description')
200 description = self.request.POST.get('description')
201 role = self.request.POST.get('role')
201 role = self.request.POST.get('role')
202
202
203 token = AuthTokenModel().create(
203 token = AuthTokenModel().create(
204 c.user.user_id, description, lifetime, role)
204 c.user.user_id, description, lifetime, role)
205 self.maybe_attach_token_scope(token)
205 self.maybe_attach_token_scope(token)
206 Session().commit()
206 Session().commit()
207
207
208 h.flash(_("Auth token successfully created"), category='success')
208 h.flash(_("Auth token successfully created"), category='success')
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
210
210
211 @LoginRequired()
211 @LoginRequired()
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 @CSRFRequired()
213 @CSRFRequired()
214 @view_config(
214 @view_config(
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
216 def auth_tokens_delete(self):
216 def auth_tokens_delete(self):
217 _ = self.request.translate
217 _ = self.request.translate
218 c = self.load_default_context()
218 c = self.load_default_context()
219
219
220 user_id = self.request.matchdict.get('user_id')
220 user_id = self.request.matchdict.get('user_id')
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
222 self._redirect_for_default_user(c.user.username)
222 self._redirect_for_default_user(c.user.username)
223
223
224 del_auth_token = self.request.POST.get('del_auth_token')
224 del_auth_token = self.request.POST.get('del_auth_token')
225
225
226 if del_auth_token:
226 if del_auth_token:
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
228 Session().commit()
228 Session().commit()
229 h.flash(_("Auth token successfully deleted"), category='success')
229 h.flash(_("Auth token successfully deleted"), category='success')
230
230
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
232
232
233 @LoginRequired()
233 @LoginRequired()
234 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
235 @view_config(
235 @view_config(
236 route_name='edit_user_groups_management', request_method='GET',
236 route_name='edit_user_groups_management', request_method='GET',
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
238 def groups_management(self):
238 def groups_management(self):
239 c = self.load_default_context()
239 c = self.load_default_context()
240
240
241 user_id = self.request.matchdict.get('user_id')
241 user_id = self.request.matchdict.get('user_id')
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
243 c.data = c.user.group_member
243 c.data = c.user.group_member
244 self._redirect_for_default_user(c.user.username)
244 self._redirect_for_default_user(c.user.username)
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
246 for group in c.user.group_member]
246 c.groups = json.dumps(groups)
247 c.groups = json.dumps(groups)
247 c.active = 'groups'
248 c.active = 'groups'
248
249
249 return self._get_template_context(c)
250 return self._get_template_context(c)
250
251
251 @LoginRequired()
252 @LoginRequired()
252 @HasPermissionAllDecorator('hg.admin')
253 @HasPermissionAllDecorator('hg.admin')
253 @view_config(
254 @view_config(
254 route_name='edit_user_groups_management_updates', request_method='POST')
255 route_name='edit_user_groups_management_updates', request_method='POST')
255 def groups_management_updates(self):
256 def groups_management_updates(self):
256 _ = self.request.translate
257 _ = self.request.translate
257 c = self.load_default_context()
258 c = self.load_default_context()
258
259
259 user_id = self.request.matchdict.get('user_id')
260 user_id = self.request.matchdict.get('user_id')
260 c.user = User.get_or_404(user_id, pyramid_exc=True)
261 c.user = User.get_or_404(user_id, pyramid_exc=True)
261 self._redirect_for_default_user(c.user.username)
262 self._redirect_for_default_user(c.user.username)
262
263
263 users_groups = set(self.request.POST.getall('users_group_id'))
264 users_groups = set(self.request.POST.getall('users_group_id'))
264 users_groups_model = []
265 users_groups_model = []
265
266
266 for ugid in users_groups:
267 for ugid in users_groups:
267 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
268 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
268 user_group_model = UserGroupModel()
269 user_group_model = UserGroupModel()
269 user_group_model.change_groups(c.user, users_groups_model)
270 user_group_model.change_groups(c.user, users_groups_model)
270
271
271 Session().commit()
272 Session().commit()
272 c.active = 'user_groups_management'
273 c.active = 'user_groups_management'
273 h.flash(_("Groups successfully changed"), category='success')
274 h.flash(_("Groups successfully changed"), category='success')
274
275
275 return HTTPFound(h.route_path(
276 return HTTPFound(h.route_path(
276 'edit_user_groups_management', user_id=user_id))
277 'edit_user_groups_management', user_id=user_id))
277
278
278 @LoginRequired()
279 @LoginRequired()
279 @HasPermissionAllDecorator('hg.admin')
280 @HasPermissionAllDecorator('hg.admin')
280 @view_config(
281 @view_config(
281 route_name='edit_user_audit_logs', request_method='GET',
282 route_name='edit_user_audit_logs', request_method='GET',
282 renderer='rhodecode:templates/admin/users/user_edit.mako')
283 renderer='rhodecode:templates/admin/users/user_edit.mako')
283 def user_audit_logs(self):
284 def user_audit_logs(self):
284 _ = self.request.translate
285 _ = self.request.translate
285 c = self.load_default_context()
286 c = self.load_default_context()
286
287
287 user_id = self.request.matchdict.get('user_id')
288 user_id = self.request.matchdict.get('user_id')
288 c.user = User.get_or_404(user_id, pyramid_exc=True)
289 c.user = User.get_or_404(user_id, pyramid_exc=True)
289 self._redirect_for_default_user(c.user.username)
290 self._redirect_for_default_user(c.user.username)
290 c.active = 'audit'
291 c.active = 'audit'
291
292
292 p = safe_int(self.request.GET.get('page', 1), 1)
293 p = safe_int(self.request.GET.get('page', 1), 1)
293
294
294 filter_term = self.request.GET.get('filter')
295 filter_term = self.request.GET.get('filter')
295 c.user_log = UserModel().get_user_log(c.user, filter_term)
296 c.user_log = UserModel().get_user_log(c.user, filter_term)
296
297
297 def url_generator(**kw):
298 def url_generator(**kw):
298 if filter_term:
299 if filter_term:
299 kw['filter'] = filter_term
300 kw['filter'] = filter_term
300 return self.request.current_route_path(_query=kw)
301 return self.request.current_route_path(_query=kw)
301
302
302 c.user_log = Page(c.user_log, page=p, items_per_page=10,
303 c.user_log = Page(c.user_log, page=p, items_per_page=10,
303 url=url_generator)
304 url=url_generator)
304 c.filter_term = filter_term
305 c.filter_term = filter_term
305 return self._get_template_context(c)
306 return self._get_template_context(c)
306
307
@@ -1,237 +1,236 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 from rhodecode.lib.index import searcher_from_config
29 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.utils2 import safe_unicode, str2bool
30 from rhodecode.lib.utils2 import safe_unicode, str2bool
31 from rhodecode.model.db import func, Repository, RepoGroup
31 from rhodecode.model.db import func, Repository, RepoGroup
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
34
34 from rhodecode.model.user_group import UserGroupModel
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class HomeView(BaseAppView):
39 class HomeView(BaseAppView):
40
40
41 def load_default_context(self):
41 def load_default_context(self):
42 c = self._get_local_tmpl_context()
42 c = self._get_local_tmpl_context()
43 c.user = c.auth_user.get_instance()
43 c.user = c.auth_user.get_instance()
44 self._register_global_c(c)
44 self._register_global_c(c)
45 return c
45 return c
46
46
47 @LoginRequired()
47 @LoginRequired()
48 @view_config(
48 @view_config(
49 route_name='user_autocomplete_data', request_method='GET',
49 route_name='user_autocomplete_data', request_method='GET',
50 renderer='json_ext', xhr=True)
50 renderer='json_ext', xhr=True)
51 def user_autocomplete_data(self):
51 def user_autocomplete_data(self):
52 query = self.request.GET.get('query')
52 query = self.request.GET.get('query')
53 active = str2bool(self.request.GET.get('active') or True)
53 active = str2bool(self.request.GET.get('active') or True)
54 include_groups = str2bool(self.request.GET.get('user_groups'))
54 include_groups = str2bool(self.request.GET.get('user_groups'))
55
55
56 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
56 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
57 query, active, include_groups)
57 query, active, include_groups)
58
58
59 repo_model = RepoModel()
59 repo_model = RepoModel()
60 _users = repo_model.get_users(
60 _users = repo_model.get_users(
61 name_contains=query, only_active=active)
61 name_contains=query, only_active=active)
62
62
63 if include_groups:
63 if include_groups:
64 # extend with user groups
64 # extend with user groups
65 _user_groups = repo_model.get_user_groups(
65 _user_groups = UserGroupModel().get_user_groups(
66 name_contains=query, only_active=active)
66 name_contains=query, only_active=active)
67 _users = _users + _user_groups
67 _users = _users + _user_groups
68
68
69 return {'suggestions': _users}
69 return {'suggestions': _users}
70
70
71 @LoginRequired()
71 @LoginRequired()
72 @NotAnonymous()
72 @NotAnonymous()
73 @view_config(
73 @view_config(
74 route_name='user_group_autocomplete_data', request_method='GET',
74 route_name='user_group_autocomplete_data', request_method='GET',
75 renderer='json_ext', xhr=True)
75 renderer='json_ext', xhr=True)
76 def user_group_autocomplete_data(self):
76 def user_group_autocomplete_data(self):
77 query = self.request.GET.get('query')
77 query = self.request.GET.get('query')
78 active = str2bool(self.request.GET.get('active') or True)
78 active = str2bool(self.request.GET.get('active') or True)
79 log.debug('generating user group list, query:%s, active:%s',
79 log.debug('generating user group list, query:%s, active:%s',
80 query, active)
80 query, active)
81
81
82 repo_model = RepoModel()
82 _user_groups = UserGroupModel().get_user_groups(
83 _user_groups = repo_model.get_user_groups(
84 name_contains=query, only_active=active)
83 name_contains=query, only_active=active)
85 _user_groups = _user_groups
84 _user_groups = _user_groups
86
85
87 return {'suggestions': _user_groups}
86 return {'suggestions': _user_groups}
88
87
89 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
88 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
90 query = Repository.query()\
89 query = Repository.query()\
91 .order_by(func.length(Repository.repo_name))\
90 .order_by(func.length(Repository.repo_name))\
92 .order_by(Repository.repo_name)
91 .order_by(Repository.repo_name)
93
92
94 if repo_type:
93 if repo_type:
95 query = query.filter(Repository.repo_type == repo_type)
94 query = query.filter(Repository.repo_type == repo_type)
96
95
97 if name_contains:
96 if name_contains:
98 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
97 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
99 query = query.filter(
98 query = query.filter(
100 Repository.repo_name.ilike(ilike_expression))
99 Repository.repo_name.ilike(ilike_expression))
101 query = query.limit(limit)
100 query = query.limit(limit)
102
101
103 all_repos = query.all()
102 all_repos = query.all()
104 # permission checks are inside this function
103 # permission checks are inside this function
105 repo_iter = ScmModel().get_repos(all_repos)
104 repo_iter = ScmModel().get_repos(all_repos)
106 return [
105 return [
107 {
106 {
108 'id': obj['name'],
107 'id': obj['name'],
109 'text': obj['name'],
108 'text': obj['name'],
110 'type': 'repo',
109 'type': 'repo',
111 'obj': obj['dbrepo'],
110 'obj': obj['dbrepo'],
112 'url': h.url('summary_home', repo_name=obj['name'])
111 'url': h.url('summary_home', repo_name=obj['name'])
113 }
112 }
114 for obj in repo_iter]
113 for obj in repo_iter]
115
114
116 def _get_repo_group_list(self, name_contains=None, limit=20):
115 def _get_repo_group_list(self, name_contains=None, limit=20):
117 query = RepoGroup.query()\
116 query = RepoGroup.query()\
118 .order_by(func.length(RepoGroup.group_name))\
117 .order_by(func.length(RepoGroup.group_name))\
119 .order_by(RepoGroup.group_name)
118 .order_by(RepoGroup.group_name)
120
119
121 if name_contains:
120 if name_contains:
122 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
121 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
123 query = query.filter(
122 query = query.filter(
124 RepoGroup.group_name.ilike(ilike_expression))
123 RepoGroup.group_name.ilike(ilike_expression))
125 query = query.limit(limit)
124 query = query.limit(limit)
126
125
127 all_groups = query.all()
126 all_groups = query.all()
128 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
127 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
129 return [
128 return [
130 {
129 {
131 'id': obj.group_name,
130 'id': obj.group_name,
132 'text': obj.group_name,
131 'text': obj.group_name,
133 'type': 'group',
132 'type': 'group',
134 'obj': {},
133 'obj': {},
135 'url': h.url('repo_group_home', group_name=obj.group_name)
134 'url': h.url('repo_group_home', group_name=obj.group_name)
136 }
135 }
137 for obj in repo_groups_iter]
136 for obj in repo_groups_iter]
138
137
139 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
138 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
140 if not hash_starts_with or len(hash_starts_with) < 3:
139 if not hash_starts_with or len(hash_starts_with) < 3:
141 return []
140 return []
142
141
143 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
142 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
144
143
145 if len(commit_hashes) != 1:
144 if len(commit_hashes) != 1:
146 return []
145 return []
147
146
148 commit_hash_prefix = commit_hashes[0]
147 commit_hash_prefix = commit_hashes[0]
149
148
150 searcher = searcher_from_config(self.request.registry.settings)
149 searcher = searcher_from_config(self.request.registry.settings)
151 result = searcher.search(
150 result = searcher.search(
152 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
151 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
153 raise_on_exc=False)
152 raise_on_exc=False)
154
153
155 return [
154 return [
156 {
155 {
157 'id': entry['commit_id'],
156 'id': entry['commit_id'],
158 'text': entry['commit_id'],
157 'text': entry['commit_id'],
159 'type': 'commit',
158 'type': 'commit',
160 'obj': {'repo': entry['repository']},
159 'obj': {'repo': entry['repository']},
161 'url': h.url('changeset_home',
160 'url': h.url('changeset_home',
162 repo_name=entry['repository'],
161 repo_name=entry['repository'],
163 revision=entry['commit_id'])
162 revision=entry['commit_id'])
164 }
163 }
165 for entry in result['results']]
164 for entry in result['results']]
166
165
167 @LoginRequired()
166 @LoginRequired()
168 @view_config(
167 @view_config(
169 route_name='repo_list_data', request_method='GET',
168 route_name='repo_list_data', request_method='GET',
170 renderer='json_ext', xhr=True)
169 renderer='json_ext', xhr=True)
171 def repo_list_data(self):
170 def repo_list_data(self):
172 _ = self.request.translate
171 _ = self.request.translate
173
172
174 query = self.request.GET.get('query')
173 query = self.request.GET.get('query')
175 repo_type = self.request.GET.get('repo_type')
174 repo_type = self.request.GET.get('repo_type')
176 log.debug('generating repo list, query:%s, repo_type:%s',
175 log.debug('generating repo list, query:%s, repo_type:%s',
177 query, repo_type)
176 query, repo_type)
178
177
179 res = []
178 res = []
180 repos = self._get_repo_list(query, repo_type=repo_type)
179 repos = self._get_repo_list(query, repo_type=repo_type)
181 if repos:
180 if repos:
182 res.append({
181 res.append({
183 'text': _('Repositories'),
182 'text': _('Repositories'),
184 'children': repos
183 'children': repos
185 })
184 })
186
185
187 data = {
186 data = {
188 'more': False,
187 'more': False,
189 'results': res
188 'results': res
190 }
189 }
191 return data
190 return data
192
191
193 @LoginRequired()
192 @LoginRequired()
194 @view_config(
193 @view_config(
195 route_name='goto_switcher_data', request_method='GET',
194 route_name='goto_switcher_data', request_method='GET',
196 renderer='json_ext', xhr=True)
195 renderer='json_ext', xhr=True)
197 def goto_switcher_data(self):
196 def goto_switcher_data(self):
198 c = self.load_default_context()
197 c = self.load_default_context()
199
198
200 _ = self.request.translate
199 _ = self.request.translate
201
200
202 query = self.request.GET.get('query')
201 query = self.request.GET.get('query')
203 log.debug('generating goto switcher list, query %s', query)
202 log.debug('generating goto switcher list, query %s', query)
204
203
205 res = []
204 res = []
206 repo_groups = self._get_repo_group_list(query)
205 repo_groups = self._get_repo_group_list(query)
207 if repo_groups:
206 if repo_groups:
208 res.append({
207 res.append({
209 'text': _('Groups'),
208 'text': _('Groups'),
210 'children': repo_groups
209 'children': repo_groups
211 })
210 })
212
211
213 repos = self._get_repo_list(query)
212 repos = self._get_repo_list(query)
214 if repos:
213 if repos:
215 res.append({
214 res.append({
216 'text': _('Repositories'),
215 'text': _('Repositories'),
217 'children': repos
216 'children': repos
218 })
217 })
219
218
220 commits = self._get_hash_commit_list(c.auth_user, query)
219 commits = self._get_hash_commit_list(c.auth_user, query)
221 if commits:
220 if commits:
222 unique_repos = {}
221 unique_repos = {}
223 for commit in commits:
222 for commit in commits:
224 unique_repos.setdefault(commit['obj']['repo'], []
223 unique_repos.setdefault(commit['obj']['repo'], []
225 ).append(commit)
224 ).append(commit)
226
225
227 for repo in unique_repos:
226 for repo in unique_repos:
228 res.append({
227 res.append({
229 'text': _('Commits in %(repo)s') % {'repo': repo},
228 'text': _('Commits in %(repo)s') % {'repo': repo},
230 'children': unique_repos[repo]
229 'children': unique_repos[repo]
231 })
230 })
232
231
233 data = {
232 data = {
234 'more': False,
233 'more': False,
235 'results': res
234 'results': res
236 }
235 }
237 return data
236 return data
@@ -1,1080 +1,1036 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Repository model for rhodecode
22 Repository model for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
32
32
33 from sqlalchemy.sql import func
34 from sqlalchemy.sql.expression import true, or_
33 from sqlalchemy.sql.expression import true, or_
35 from zope.cachedescriptors.property import Lazy as LazyProperty
34 from zope.cachedescriptors.property import Lazy as LazyProperty
36
35
37 from rhodecode import events
36 from rhodecode import events
38 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import HasUserGroupPermissionAny
38 from rhodecode.lib.auth import HasUserGroupPermissionAny
40 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.lib.exceptions import AttachedForksError
40 from rhodecode.lib.exceptions import AttachedForksError
42 from rhodecode.lib.hooks_base import log_delete_repository
41 from rhodecode.lib.hooks_base import log_delete_repository
43 from rhodecode.lib.markup_renderer import MarkupRenderer
44 from rhodecode.lib.utils import make_db_config
42 from rhodecode.lib.utils import make_db_config
45 from rhodecode.lib.utils2 import (
43 from rhodecode.lib.utils2 import (
46 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
44 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
47 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
45 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
48 from rhodecode.lib.vcs.backends import get_backend
46 from rhodecode.lib.vcs.backends import get_backend
49 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
50 from rhodecode.model import BaseModel
47 from rhodecode.model import BaseModel
51 from rhodecode.model.db import (
48 from rhodecode.model.db import (
52 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
49 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
53 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
50 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
54 RepoGroup, RepositoryField)
51 RepoGroup, RepositoryField)
55 from rhodecode.model.scm import UserGroupList
52
56 from rhodecode.model.settings import VcsSettingsModel
53 from rhodecode.model.settings import VcsSettingsModel
57
54
58
55
59 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
60
57
61
58
62 class RepoModel(BaseModel):
59 class RepoModel(BaseModel):
63
60
64 cls = Repository
61 cls = Repository
65
62
66 def _get_user_group(self, users_group):
63 def _get_user_group(self, users_group):
67 return self._get_instance(UserGroup, users_group,
64 return self._get_instance(UserGroup, users_group,
68 callback=UserGroup.get_by_group_name)
65 callback=UserGroup.get_by_group_name)
69
66
70 def _get_repo_group(self, repo_group):
67 def _get_repo_group(self, repo_group):
71 return self._get_instance(RepoGroup, repo_group,
68 return self._get_instance(RepoGroup, repo_group,
72 callback=RepoGroup.get_by_group_name)
69 callback=RepoGroup.get_by_group_name)
73
70
74 def _create_default_perms(self, repository, private):
71 def _create_default_perms(self, repository, private):
75 # create default permission
72 # create default permission
76 default = 'repository.read'
73 default = 'repository.read'
77 def_user = User.get_default_user()
74 def_user = User.get_default_user()
78 for p in def_user.user_perms:
75 for p in def_user.user_perms:
79 if p.permission.permission_name.startswith('repository.'):
76 if p.permission.permission_name.startswith('repository.'):
80 default = p.permission.permission_name
77 default = p.permission.permission_name
81 break
78 break
82
79
83 default_perm = 'repository.none' if private else default
80 default_perm = 'repository.none' if private else default
84
81
85 repo_to_perm = UserRepoToPerm()
82 repo_to_perm = UserRepoToPerm()
86 repo_to_perm.permission = Permission.get_by_key(default_perm)
83 repo_to_perm.permission = Permission.get_by_key(default_perm)
87
84
88 repo_to_perm.repository = repository
85 repo_to_perm.repository = repository
89 repo_to_perm.user_id = def_user.user_id
86 repo_to_perm.user_id = def_user.user_id
90
87
91 return repo_to_perm
88 return repo_to_perm
92
89
93 @LazyProperty
90 @LazyProperty
94 def repos_path(self):
91 def repos_path(self):
95 """
92 """
96 Gets the repositories root path from database
93 Gets the repositories root path from database
97 """
94 """
98 settings_model = VcsSettingsModel(sa=self.sa)
95 settings_model = VcsSettingsModel(sa=self.sa)
99 return settings_model.get_repos_location()
96 return settings_model.get_repos_location()
100
97
101 def get(self, repo_id, cache=False):
98 def get(self, repo_id, cache=False):
102 repo = self.sa.query(Repository) \
99 repo = self.sa.query(Repository) \
103 .filter(Repository.repo_id == repo_id)
100 .filter(Repository.repo_id == repo_id)
104
101
105 if cache:
102 if cache:
106 repo = repo.options(FromCache("sql_cache_short",
103 repo = repo.options(FromCache("sql_cache_short",
107 "get_repo_%s" % repo_id))
104 "get_repo_%s" % repo_id))
108 return repo.scalar()
105 return repo.scalar()
109
106
110 def get_repo(self, repository):
107 def get_repo(self, repository):
111 return self._get_repo(repository)
108 return self._get_repo(repository)
112
109
113 def get_by_repo_name(self, repo_name, cache=False):
110 def get_by_repo_name(self, repo_name, cache=False):
114 repo = self.sa.query(Repository) \
111 repo = self.sa.query(Repository) \
115 .filter(Repository.repo_name == repo_name)
112 .filter(Repository.repo_name == repo_name)
116
113
117 if cache:
114 if cache:
118 repo = repo.options(FromCache("sql_cache_short",
115 repo = repo.options(FromCache("sql_cache_short",
119 "get_repo_%s" % repo_name))
116 "get_repo_%s" % repo_name))
120 return repo.scalar()
117 return repo.scalar()
121
118
122 def _extract_id_from_repo_name(self, repo_name):
119 def _extract_id_from_repo_name(self, repo_name):
123 if repo_name.startswith('/'):
120 if repo_name.startswith('/'):
124 repo_name = repo_name.lstrip('/')
121 repo_name = repo_name.lstrip('/')
125 by_id_match = re.match(r'^_(\d{1,})', repo_name)
122 by_id_match = re.match(r'^_(\d{1,})', repo_name)
126 if by_id_match:
123 if by_id_match:
127 return by_id_match.groups()[0]
124 return by_id_match.groups()[0]
128
125
129 def get_repo_by_id(self, repo_name):
126 def get_repo_by_id(self, repo_name):
130 """
127 """
131 Extracts repo_name by id from special urls.
128 Extracts repo_name by id from special urls.
132 Example url is _11/repo_name
129 Example url is _11/repo_name
133
130
134 :param repo_name:
131 :param repo_name:
135 :return: repo object if matched else None
132 :return: repo object if matched else None
136 """
133 """
137 try:
134 try:
138 _repo_id = self._extract_id_from_repo_name(repo_name)
135 _repo_id = self._extract_id_from_repo_name(repo_name)
139 if _repo_id:
136 if _repo_id:
140 return self.get(_repo_id)
137 return self.get(_repo_id)
141 except Exception:
138 except Exception:
142 log.exception('Failed to extract repo_name from URL')
139 log.exception('Failed to extract repo_name from URL')
143
140
144 return None
141 return None
145
142
146 def get_repos_for_root(self, root, traverse=False):
143 def get_repos_for_root(self, root, traverse=False):
147 if traverse:
144 if traverse:
148 like_expression = u'{}%'.format(safe_unicode(root))
145 like_expression = u'{}%'.format(safe_unicode(root))
149 repos = Repository.query().filter(
146 repos = Repository.query().filter(
150 Repository.repo_name.like(like_expression)).all()
147 Repository.repo_name.like(like_expression)).all()
151 else:
148 else:
152 if root and not isinstance(root, RepoGroup):
149 if root and not isinstance(root, RepoGroup):
153 raise ValueError(
150 raise ValueError(
154 'Root must be an instance '
151 'Root must be an instance '
155 'of RepoGroup, got:{} instead'.format(type(root)))
152 'of RepoGroup, got:{} instead'.format(type(root)))
156 repos = Repository.query().filter(Repository.group == root).all()
153 repos = Repository.query().filter(Repository.group == root).all()
157 return repos
154 return repos
158
155
159 def get_url(self, repo):
156 def get_url(self, repo):
160 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
157 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
161 qualified=True)
158 qualified=True)
162
159
163 def get_users(self, name_contains=None, limit=20, only_active=True):
160 def get_users(self, name_contains=None, limit=20, only_active=True):
164
161
165 # TODO: mikhail: move this method to the UserModel.
162 # TODO: mikhail: move this method to the UserModel.
166 query = self.sa.query(User)
163 query = self.sa.query(User)
167 if only_active:
164 if only_active:
168 query = query.filter(User.active == true())
165 query = query.filter(User.active == true())
169
166
170 if name_contains:
167 if name_contains:
171 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
168 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
172 query = query.filter(
169 query = query.filter(
173 or_(
170 or_(
174 User.name.ilike(ilike_expression),
171 User.name.ilike(ilike_expression),
175 User.lastname.ilike(ilike_expression),
172 User.lastname.ilike(ilike_expression),
176 User.username.ilike(ilike_expression)
173 User.username.ilike(ilike_expression)
177 )
174 )
178 )
175 )
179 query = query.limit(limit)
176 query = query.limit(limit)
180 users = query.all()
177 users = query.all()
181
178
182 _users = [
179 _users = [
183 {
180 {
184 'id': user.user_id,
181 'id': user.user_id,
185 'first_name': user.name,
182 'first_name': user.name,
186 'last_name': user.lastname,
183 'last_name': user.lastname,
187 'username': user.username,
184 'username': user.username,
188 'email': user.email,
185 'email': user.email,
189 'icon_link': h.gravatar_url(user.email, 30),
186 'icon_link': h.gravatar_url(user.email, 30),
190 'value_display': h.person(user),
187 'value_display': h.person(user),
191 'value': user.username,
188 'value': user.username,
192 'value_type': 'user',
189 'value_type': 'user',
193 'active': user.active,
190 'active': user.active,
194 }
191 }
195 for user in users
192 for user in users
196 ]
193 ]
197 return _users
194 return _users
198
195
199 def get_user_groups(self, name_contains=None, limit=20, only_active=True):
200
201 # TODO: mikhail: move this method to the UserGroupModel.
202 query = self.sa.query(UserGroup)
203 if only_active:
204 query = query.filter(UserGroup.users_group_active == true())
205
206 if name_contains:
207 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
208 query = query.filter(
209 UserGroup.users_group_name.ilike(ilike_expression))\
210 .order_by(func.length(UserGroup.users_group_name))\
211 .order_by(UserGroup.users_group_name)
212
213 query = query.limit(limit)
214 user_groups = query.all()
215 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
216 user_groups = UserGroupList(user_groups, perm_set=perm_set)
217
218 _groups = [
219 {
220 'id': group.users_group_id,
221 # TODO: marcink figure out a way to generate the url for the
222 # icon
223 'icon_link': '',
224 'value_display': 'Group: %s (%d members)' % (
225 group.users_group_name, len(group.members),),
226 'value': group.users_group_name,
227 'description': group.user_group_description,
228 'owner': group.user.username,
229
230 'owner_icon': h.gravatar_url(group.user.email, 30),
231 'value_display_owner': h.person(group.user.email),
232
233 'value_type': 'user_group',
234 'active': group.users_group_active,
235 }
236 for group in user_groups
237 ]
238 return _groups
239
240 @classmethod
196 @classmethod
241 def update_repoinfo(cls, repositories=None):
197 def update_repoinfo(cls, repositories=None):
242 if not repositories:
198 if not repositories:
243 repositories = Repository.getAll()
199 repositories = Repository.getAll()
244 for repo in repositories:
200 for repo in repositories:
245 repo.update_commit_cache()
201 repo.update_commit_cache()
246
202
247 def get_repos_as_dict(self, repo_list=None, admin=False,
203 def get_repos_as_dict(self, repo_list=None, admin=False,
248 super_user_actions=False):
204 super_user_actions=False):
249
205
250 from rhodecode.lib.utils import PartialRenderer
206 from rhodecode.lib.utils import PartialRenderer
251 _render = PartialRenderer('data_table/_dt_elements.mako')
207 _render = PartialRenderer('data_table/_dt_elements.mako')
252 c = _render.c
208 c = _render.c
253
209
254 def quick_menu(repo_name):
210 def quick_menu(repo_name):
255 return _render('quick_menu', repo_name)
211 return _render('quick_menu', repo_name)
256
212
257 def repo_lnk(name, rtype, rstate, private, fork_of):
213 def repo_lnk(name, rtype, rstate, private, fork_of):
258 return _render('repo_name', name, rtype, rstate, private, fork_of,
214 return _render('repo_name', name, rtype, rstate, private, fork_of,
259 short_name=not admin, admin=False)
215 short_name=not admin, admin=False)
260
216
261 def last_change(last_change):
217 def last_change(last_change):
262 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
218 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
263 last_change = last_change + timedelta(seconds=
219 last_change = last_change + timedelta(seconds=
264 (datetime.now() - datetime.utcnow()).seconds)
220 (datetime.now() - datetime.utcnow()).seconds)
265 return _render("last_change", last_change)
221 return _render("last_change", last_change)
266
222
267 def rss_lnk(repo_name):
223 def rss_lnk(repo_name):
268 return _render("rss", repo_name)
224 return _render("rss", repo_name)
269
225
270 def atom_lnk(repo_name):
226 def atom_lnk(repo_name):
271 return _render("atom", repo_name)
227 return _render("atom", repo_name)
272
228
273 def last_rev(repo_name, cs_cache):
229 def last_rev(repo_name, cs_cache):
274 return _render('revision', repo_name, cs_cache.get('revision'),
230 return _render('revision', repo_name, cs_cache.get('revision'),
275 cs_cache.get('raw_id'), cs_cache.get('author'),
231 cs_cache.get('raw_id'), cs_cache.get('author'),
276 cs_cache.get('message'))
232 cs_cache.get('message'))
277
233
278 def desc(desc):
234 def desc(desc):
279 if c.visual.stylify_metatags:
235 if c.visual.stylify_metatags:
280 desc = h.urlify_text(h.escaped_stylize(desc))
236 desc = h.urlify_text(h.escaped_stylize(desc))
281 else:
237 else:
282 desc = h.urlify_text(h.html_escape(desc))
238 desc = h.urlify_text(h.html_escape(desc))
283
239
284 return _render('repo_desc', desc)
240 return _render('repo_desc', desc)
285
241
286 def state(repo_state):
242 def state(repo_state):
287 return _render("repo_state", repo_state)
243 return _render("repo_state", repo_state)
288
244
289 def repo_actions(repo_name):
245 def repo_actions(repo_name):
290 return _render('repo_actions', repo_name, super_user_actions)
246 return _render('repo_actions', repo_name, super_user_actions)
291
247
292 def user_profile(username):
248 def user_profile(username):
293 return _render('user_profile', username)
249 return _render('user_profile', username)
294
250
295 repos_data = []
251 repos_data = []
296 for repo in repo_list:
252 for repo in repo_list:
297 cs_cache = repo.changeset_cache
253 cs_cache = repo.changeset_cache
298 row = {
254 row = {
299 "menu": quick_menu(repo.repo_name),
255 "menu": quick_menu(repo.repo_name),
300
256
301 "name": repo_lnk(repo.repo_name, repo.repo_type,
257 "name": repo_lnk(repo.repo_name, repo.repo_type,
302 repo.repo_state, repo.private, repo.fork),
258 repo.repo_state, repo.private, repo.fork),
303 "name_raw": repo.repo_name.lower(),
259 "name_raw": repo.repo_name.lower(),
304
260
305 "last_change": last_change(repo.last_db_change),
261 "last_change": last_change(repo.last_db_change),
306 "last_change_raw": datetime_to_time(repo.last_db_change),
262 "last_change_raw": datetime_to_time(repo.last_db_change),
307
263
308 "last_changeset": last_rev(repo.repo_name, cs_cache),
264 "last_changeset": last_rev(repo.repo_name, cs_cache),
309 "last_changeset_raw": cs_cache.get('revision'),
265 "last_changeset_raw": cs_cache.get('revision'),
310
266
311 "desc": desc(repo.description),
267 "desc": desc(repo.description),
312 "owner": user_profile(repo.user.username),
268 "owner": user_profile(repo.user.username),
313
269
314 "state": state(repo.repo_state),
270 "state": state(repo.repo_state),
315 "rss": rss_lnk(repo.repo_name),
271 "rss": rss_lnk(repo.repo_name),
316
272
317 "atom": atom_lnk(repo.repo_name),
273 "atom": atom_lnk(repo.repo_name),
318 }
274 }
319 if admin:
275 if admin:
320 row.update({
276 row.update({
321 "action": repo_actions(repo.repo_name),
277 "action": repo_actions(repo.repo_name),
322 })
278 })
323 repos_data.append(row)
279 repos_data.append(row)
324
280
325 return repos_data
281 return repos_data
326
282
327 def _get_defaults(self, repo_name):
283 def _get_defaults(self, repo_name):
328 """
284 """
329 Gets information about repository, and returns a dict for
285 Gets information about repository, and returns a dict for
330 usage in forms
286 usage in forms
331
287
332 :param repo_name:
288 :param repo_name:
333 """
289 """
334
290
335 repo_info = Repository.get_by_repo_name(repo_name)
291 repo_info = Repository.get_by_repo_name(repo_name)
336
292
337 if repo_info is None:
293 if repo_info is None:
338 return None
294 return None
339
295
340 defaults = repo_info.get_dict()
296 defaults = repo_info.get_dict()
341 defaults['repo_name'] = repo_info.just_name
297 defaults['repo_name'] = repo_info.just_name
342
298
343 groups = repo_info.groups_with_parents
299 groups = repo_info.groups_with_parents
344 parent_group = groups[-1] if groups else None
300 parent_group = groups[-1] if groups else None
345
301
346 # we use -1 as this is how in HTML, we mark an empty group
302 # we use -1 as this is how in HTML, we mark an empty group
347 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
303 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
348
304
349 keys_to_process = (
305 keys_to_process = (
350 {'k': 'repo_type', 'strip': False},
306 {'k': 'repo_type', 'strip': False},
351 {'k': 'repo_enable_downloads', 'strip': True},
307 {'k': 'repo_enable_downloads', 'strip': True},
352 {'k': 'repo_description', 'strip': True},
308 {'k': 'repo_description', 'strip': True},
353 {'k': 'repo_enable_locking', 'strip': True},
309 {'k': 'repo_enable_locking', 'strip': True},
354 {'k': 'repo_landing_rev', 'strip': True},
310 {'k': 'repo_landing_rev', 'strip': True},
355 {'k': 'clone_uri', 'strip': False},
311 {'k': 'clone_uri', 'strip': False},
356 {'k': 'repo_private', 'strip': True},
312 {'k': 'repo_private', 'strip': True},
357 {'k': 'repo_enable_statistics', 'strip': True}
313 {'k': 'repo_enable_statistics', 'strip': True}
358 )
314 )
359
315
360 for item in keys_to_process:
316 for item in keys_to_process:
361 attr = item['k']
317 attr = item['k']
362 if item['strip']:
318 if item['strip']:
363 attr = remove_prefix(item['k'], 'repo_')
319 attr = remove_prefix(item['k'], 'repo_')
364
320
365 val = defaults[attr]
321 val = defaults[attr]
366 if item['k'] == 'repo_landing_rev':
322 if item['k'] == 'repo_landing_rev':
367 val = ':'.join(defaults[attr])
323 val = ':'.join(defaults[attr])
368 defaults[item['k']] = val
324 defaults[item['k']] = val
369 if item['k'] == 'clone_uri':
325 if item['k'] == 'clone_uri':
370 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
326 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
371
327
372 # fill owner
328 # fill owner
373 if repo_info.user:
329 if repo_info.user:
374 defaults.update({'user': repo_info.user.username})
330 defaults.update({'user': repo_info.user.username})
375 else:
331 else:
376 replacement_user = User.get_first_super_admin().username
332 replacement_user = User.get_first_super_admin().username
377 defaults.update({'user': replacement_user})
333 defaults.update({'user': replacement_user})
378
334
379 # fill repository users
335 # fill repository users
380 for p in repo_info.repo_to_perm:
336 for p in repo_info.repo_to_perm:
381 defaults.update({'u_perm_%s' % p.user.user_id:
337 defaults.update({'u_perm_%s' % p.user.user_id:
382 p.permission.permission_name})
338 p.permission.permission_name})
383
339
384 # fill repository groups
340 # fill repository groups
385 for p in repo_info.users_group_to_perm:
341 for p in repo_info.users_group_to_perm:
386 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
342 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
387 p.permission.permission_name})
343 p.permission.permission_name})
388
344
389 return defaults
345 return defaults
390
346
391 def update(self, repo, **kwargs):
347 def update(self, repo, **kwargs):
392 try:
348 try:
393 cur_repo = self._get_repo(repo)
349 cur_repo = self._get_repo(repo)
394 source_repo_name = cur_repo.repo_name
350 source_repo_name = cur_repo.repo_name
395 if 'user' in kwargs:
351 if 'user' in kwargs:
396 cur_repo.user = User.get_by_username(kwargs['user'])
352 cur_repo.user = User.get_by_username(kwargs['user'])
397
353
398 if 'repo_group' in kwargs:
354 if 'repo_group' in kwargs:
399 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
355 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
400 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
356 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
401
357
402 update_keys = [
358 update_keys = [
403 (1, 'repo_description'),
359 (1, 'repo_description'),
404 (1, 'repo_landing_rev'),
360 (1, 'repo_landing_rev'),
405 (1, 'repo_private'),
361 (1, 'repo_private'),
406 (1, 'repo_enable_downloads'),
362 (1, 'repo_enable_downloads'),
407 (1, 'repo_enable_locking'),
363 (1, 'repo_enable_locking'),
408 (1, 'repo_enable_statistics'),
364 (1, 'repo_enable_statistics'),
409 (0, 'clone_uri'),
365 (0, 'clone_uri'),
410 (0, 'fork_id')
366 (0, 'fork_id')
411 ]
367 ]
412 for strip, k in update_keys:
368 for strip, k in update_keys:
413 if k in kwargs:
369 if k in kwargs:
414 val = kwargs[k]
370 val = kwargs[k]
415 if strip:
371 if strip:
416 k = remove_prefix(k, 'repo_')
372 k = remove_prefix(k, 'repo_')
417 if k == 'clone_uri':
373 if k == 'clone_uri':
418 from rhodecode.model.validators import Missing
374 from rhodecode.model.validators import Missing
419 _change = kwargs.get('clone_uri_change')
375 _change = kwargs.get('clone_uri_change')
420 if _change in [Missing, 'OLD']:
376 if _change in [Missing, 'OLD']:
421 # we don't change the value, so use original one
377 # we don't change the value, so use original one
422 val = cur_repo.clone_uri
378 val = cur_repo.clone_uri
423
379
424 setattr(cur_repo, k, val)
380 setattr(cur_repo, k, val)
425
381
426 new_name = cur_repo.get_new_name(kwargs['repo_name'])
382 new_name = cur_repo.get_new_name(kwargs['repo_name'])
427 cur_repo.repo_name = new_name
383 cur_repo.repo_name = new_name
428
384
429 # if private flag is set, reset default permission to NONE
385 # if private flag is set, reset default permission to NONE
430 if kwargs.get('repo_private'):
386 if kwargs.get('repo_private'):
431 EMPTY_PERM = 'repository.none'
387 EMPTY_PERM = 'repository.none'
432 RepoModel().grant_user_permission(
388 RepoModel().grant_user_permission(
433 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
389 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
434 )
390 )
435
391
436 # handle extra fields
392 # handle extra fields
437 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
393 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
438 kwargs):
394 kwargs):
439 k = RepositoryField.un_prefix_key(field)
395 k = RepositoryField.un_prefix_key(field)
440 ex_field = RepositoryField.get_by_key_name(
396 ex_field = RepositoryField.get_by_key_name(
441 key=k, repo=cur_repo)
397 key=k, repo=cur_repo)
442 if ex_field:
398 if ex_field:
443 ex_field.field_value = kwargs[field]
399 ex_field.field_value = kwargs[field]
444 self.sa.add(ex_field)
400 self.sa.add(ex_field)
445 self.sa.add(cur_repo)
401 self.sa.add(cur_repo)
446
402
447 if source_repo_name != new_name:
403 if source_repo_name != new_name:
448 # rename repository
404 # rename repository
449 self._rename_filesystem_repo(
405 self._rename_filesystem_repo(
450 old=source_repo_name, new=new_name)
406 old=source_repo_name, new=new_name)
451
407
452 return cur_repo
408 return cur_repo
453 except Exception:
409 except Exception:
454 log.error(traceback.format_exc())
410 log.error(traceback.format_exc())
455 raise
411 raise
456
412
457 def _create_repo(self, repo_name, repo_type, description, owner,
413 def _create_repo(self, repo_name, repo_type, description, owner,
458 private=False, clone_uri=None, repo_group=None,
414 private=False, clone_uri=None, repo_group=None,
459 landing_rev='rev:tip', fork_of=None,
415 landing_rev='rev:tip', fork_of=None,
460 copy_fork_permissions=False, enable_statistics=False,
416 copy_fork_permissions=False, enable_statistics=False,
461 enable_locking=False, enable_downloads=False,
417 enable_locking=False, enable_downloads=False,
462 copy_group_permissions=False,
418 copy_group_permissions=False,
463 state=Repository.STATE_PENDING):
419 state=Repository.STATE_PENDING):
464 """
420 """
465 Create repository inside database with PENDING state, this should be
421 Create repository inside database with PENDING state, this should be
466 only executed by create() repo. With exception of importing existing
422 only executed by create() repo. With exception of importing existing
467 repos
423 repos
468 """
424 """
469 from rhodecode.model.scm import ScmModel
425 from rhodecode.model.scm import ScmModel
470
426
471 owner = self._get_user(owner)
427 owner = self._get_user(owner)
472 fork_of = self._get_repo(fork_of)
428 fork_of = self._get_repo(fork_of)
473 repo_group = self._get_repo_group(safe_int(repo_group))
429 repo_group = self._get_repo_group(safe_int(repo_group))
474
430
475 try:
431 try:
476 repo_name = safe_unicode(repo_name)
432 repo_name = safe_unicode(repo_name)
477 description = safe_unicode(description)
433 description = safe_unicode(description)
478 # repo name is just a name of repository
434 # repo name is just a name of repository
479 # while repo_name_full is a full qualified name that is combined
435 # while repo_name_full is a full qualified name that is combined
480 # with name and path of group
436 # with name and path of group
481 repo_name_full = repo_name
437 repo_name_full = repo_name
482 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
438 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
483
439
484 new_repo = Repository()
440 new_repo = Repository()
485 new_repo.repo_state = state
441 new_repo.repo_state = state
486 new_repo.enable_statistics = False
442 new_repo.enable_statistics = False
487 new_repo.repo_name = repo_name_full
443 new_repo.repo_name = repo_name_full
488 new_repo.repo_type = repo_type
444 new_repo.repo_type = repo_type
489 new_repo.user = owner
445 new_repo.user = owner
490 new_repo.group = repo_group
446 new_repo.group = repo_group
491 new_repo.description = description or repo_name
447 new_repo.description = description or repo_name
492 new_repo.private = private
448 new_repo.private = private
493 new_repo.clone_uri = clone_uri
449 new_repo.clone_uri = clone_uri
494 new_repo.landing_rev = landing_rev
450 new_repo.landing_rev = landing_rev
495
451
496 new_repo.enable_statistics = enable_statistics
452 new_repo.enable_statistics = enable_statistics
497 new_repo.enable_locking = enable_locking
453 new_repo.enable_locking = enable_locking
498 new_repo.enable_downloads = enable_downloads
454 new_repo.enable_downloads = enable_downloads
499
455
500 if repo_group:
456 if repo_group:
501 new_repo.enable_locking = repo_group.enable_locking
457 new_repo.enable_locking = repo_group.enable_locking
502
458
503 if fork_of:
459 if fork_of:
504 parent_repo = fork_of
460 parent_repo = fork_of
505 new_repo.fork = parent_repo
461 new_repo.fork = parent_repo
506
462
507 events.trigger(events.RepoPreCreateEvent(new_repo))
463 events.trigger(events.RepoPreCreateEvent(new_repo))
508
464
509 self.sa.add(new_repo)
465 self.sa.add(new_repo)
510
466
511 EMPTY_PERM = 'repository.none'
467 EMPTY_PERM = 'repository.none'
512 if fork_of and copy_fork_permissions:
468 if fork_of and copy_fork_permissions:
513 repo = fork_of
469 repo = fork_of
514 user_perms = UserRepoToPerm.query() \
470 user_perms = UserRepoToPerm.query() \
515 .filter(UserRepoToPerm.repository == repo).all()
471 .filter(UserRepoToPerm.repository == repo).all()
516 group_perms = UserGroupRepoToPerm.query() \
472 group_perms = UserGroupRepoToPerm.query() \
517 .filter(UserGroupRepoToPerm.repository == repo).all()
473 .filter(UserGroupRepoToPerm.repository == repo).all()
518
474
519 for perm in user_perms:
475 for perm in user_perms:
520 UserRepoToPerm.create(
476 UserRepoToPerm.create(
521 perm.user, new_repo, perm.permission)
477 perm.user, new_repo, perm.permission)
522
478
523 for perm in group_perms:
479 for perm in group_perms:
524 UserGroupRepoToPerm.create(
480 UserGroupRepoToPerm.create(
525 perm.users_group, new_repo, perm.permission)
481 perm.users_group, new_repo, perm.permission)
526 # in case we copy permissions and also set this repo to private
482 # in case we copy permissions and also set this repo to private
527 # override the default user permission to make it a private
483 # override the default user permission to make it a private
528 # repo
484 # repo
529 if private:
485 if private:
530 RepoModel(self.sa).grant_user_permission(
486 RepoModel(self.sa).grant_user_permission(
531 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
487 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
532
488
533 elif repo_group and copy_group_permissions:
489 elif repo_group and copy_group_permissions:
534 user_perms = UserRepoGroupToPerm.query() \
490 user_perms = UserRepoGroupToPerm.query() \
535 .filter(UserRepoGroupToPerm.group == repo_group).all()
491 .filter(UserRepoGroupToPerm.group == repo_group).all()
536
492
537 group_perms = UserGroupRepoGroupToPerm.query() \
493 group_perms = UserGroupRepoGroupToPerm.query() \
538 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
494 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
539
495
540 for perm in user_perms:
496 for perm in user_perms:
541 perm_name = perm.permission.permission_name.replace(
497 perm_name = perm.permission.permission_name.replace(
542 'group.', 'repository.')
498 'group.', 'repository.')
543 perm_obj = Permission.get_by_key(perm_name)
499 perm_obj = Permission.get_by_key(perm_name)
544 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
500 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
545
501
546 for perm in group_perms:
502 for perm in group_perms:
547 perm_name = perm.permission.permission_name.replace(
503 perm_name = perm.permission.permission_name.replace(
548 'group.', 'repository.')
504 'group.', 'repository.')
549 perm_obj = Permission.get_by_key(perm_name)
505 perm_obj = Permission.get_by_key(perm_name)
550 UserGroupRepoToPerm.create(
506 UserGroupRepoToPerm.create(
551 perm.users_group, new_repo, perm_obj)
507 perm.users_group, new_repo, perm_obj)
552
508
553 if private:
509 if private:
554 RepoModel(self.sa).grant_user_permission(
510 RepoModel(self.sa).grant_user_permission(
555 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
511 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
556
512
557 else:
513 else:
558 perm_obj = self._create_default_perms(new_repo, private)
514 perm_obj = self._create_default_perms(new_repo, private)
559 self.sa.add(perm_obj)
515 self.sa.add(perm_obj)
560
516
561 # now automatically start following this repository as owner
517 # now automatically start following this repository as owner
562 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
518 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
563 owner.user_id)
519 owner.user_id)
564
520
565 # we need to flush here, in order to check if database won't
521 # we need to flush here, in order to check if database won't
566 # throw any exceptions, create filesystem dirs at the very end
522 # throw any exceptions, create filesystem dirs at the very end
567 self.sa.flush()
523 self.sa.flush()
568 events.trigger(events.RepoCreateEvent(new_repo))
524 events.trigger(events.RepoCreateEvent(new_repo))
569 return new_repo
525 return new_repo
570
526
571 except Exception:
527 except Exception:
572 log.error(traceback.format_exc())
528 log.error(traceback.format_exc())
573 raise
529 raise
574
530
575 def create(self, form_data, cur_user):
531 def create(self, form_data, cur_user):
576 """
532 """
577 Create repository using celery tasks
533 Create repository using celery tasks
578
534
579 :param form_data:
535 :param form_data:
580 :param cur_user:
536 :param cur_user:
581 """
537 """
582 from rhodecode.lib.celerylib import tasks, run_task
538 from rhodecode.lib.celerylib import tasks, run_task
583 return run_task(tasks.create_repo, form_data, cur_user)
539 return run_task(tasks.create_repo, form_data, cur_user)
584
540
585 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
541 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
586 perm_deletions=None, check_perms=True,
542 perm_deletions=None, check_perms=True,
587 cur_user=None):
543 cur_user=None):
588 if not perm_additions:
544 if not perm_additions:
589 perm_additions = []
545 perm_additions = []
590 if not perm_updates:
546 if not perm_updates:
591 perm_updates = []
547 perm_updates = []
592 if not perm_deletions:
548 if not perm_deletions:
593 perm_deletions = []
549 perm_deletions = []
594
550
595 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
551 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
596
552
597 # update permissions
553 # update permissions
598 for member_id, perm, member_type in perm_updates:
554 for member_id, perm, member_type in perm_updates:
599 member_id = int(member_id)
555 member_id = int(member_id)
600 if member_type == 'user':
556 if member_type == 'user':
601 # this updates also current one if found
557 # this updates also current one if found
602 self.grant_user_permission(
558 self.grant_user_permission(
603 repo=repo, user=member_id, perm=perm)
559 repo=repo, user=member_id, perm=perm)
604 else: # set for user group
560 else: # set for user group
605 # check if we have permissions to alter this usergroup
561 # check if we have permissions to alter this usergroup
606 member_name = UserGroup.get(member_id).users_group_name
562 member_name = UserGroup.get(member_id).users_group_name
607 if not check_perms or HasUserGroupPermissionAny(
563 if not check_perms or HasUserGroupPermissionAny(
608 *req_perms)(member_name, user=cur_user):
564 *req_perms)(member_name, user=cur_user):
609 self.grant_user_group_permission(
565 self.grant_user_group_permission(
610 repo=repo, group_name=member_id, perm=perm)
566 repo=repo, group_name=member_id, perm=perm)
611
567
612 # set new permissions
568 # set new permissions
613 for member_id, perm, member_type in perm_additions:
569 for member_id, perm, member_type in perm_additions:
614 member_id = int(member_id)
570 member_id = int(member_id)
615 if member_type == 'user':
571 if member_type == 'user':
616 self.grant_user_permission(
572 self.grant_user_permission(
617 repo=repo, user=member_id, perm=perm)
573 repo=repo, user=member_id, perm=perm)
618 else: # set for user group
574 else: # set for user group
619 # check if we have permissions to alter this usergroup
575 # check if we have permissions to alter this usergroup
620 member_name = UserGroup.get(member_id).users_group_name
576 member_name = UserGroup.get(member_id).users_group_name
621 if not check_perms or HasUserGroupPermissionAny(
577 if not check_perms or HasUserGroupPermissionAny(
622 *req_perms)(member_name, user=cur_user):
578 *req_perms)(member_name, user=cur_user):
623 self.grant_user_group_permission(
579 self.grant_user_group_permission(
624 repo=repo, group_name=member_id, perm=perm)
580 repo=repo, group_name=member_id, perm=perm)
625
581
626 # delete permissions
582 # delete permissions
627 for member_id, perm, member_type in perm_deletions:
583 for member_id, perm, member_type in perm_deletions:
628 member_id = int(member_id)
584 member_id = int(member_id)
629 if member_type == 'user':
585 if member_type == 'user':
630 self.revoke_user_permission(repo=repo, user=member_id)
586 self.revoke_user_permission(repo=repo, user=member_id)
631 else: # set for user group
587 else: # set for user group
632 # check if we have permissions to alter this usergroup
588 # check if we have permissions to alter this usergroup
633 member_name = UserGroup.get(member_id).users_group_name
589 member_name = UserGroup.get(member_id).users_group_name
634 if not check_perms or HasUserGroupPermissionAny(
590 if not check_perms or HasUserGroupPermissionAny(
635 *req_perms)(member_name, user=cur_user):
591 *req_perms)(member_name, user=cur_user):
636 self.revoke_user_group_permission(
592 self.revoke_user_group_permission(
637 repo=repo, group_name=member_id)
593 repo=repo, group_name=member_id)
638
594
639 def create_fork(self, form_data, cur_user):
595 def create_fork(self, form_data, cur_user):
640 """
596 """
641 Simple wrapper into executing celery task for fork creation
597 Simple wrapper into executing celery task for fork creation
642
598
643 :param form_data:
599 :param form_data:
644 :param cur_user:
600 :param cur_user:
645 """
601 """
646 from rhodecode.lib.celerylib import tasks, run_task
602 from rhodecode.lib.celerylib import tasks, run_task
647 return run_task(tasks.create_repo_fork, form_data, cur_user)
603 return run_task(tasks.create_repo_fork, form_data, cur_user)
648
604
649 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
605 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
650 """
606 """
651 Delete given repository, forks parameter defines what do do with
607 Delete given repository, forks parameter defines what do do with
652 attached forks. Throws AttachedForksError if deleted repo has attached
608 attached forks. Throws AttachedForksError if deleted repo has attached
653 forks
609 forks
654
610
655 :param repo:
611 :param repo:
656 :param forks: str 'delete' or 'detach'
612 :param forks: str 'delete' or 'detach'
657 :param fs_remove: remove(archive) repo from filesystem
613 :param fs_remove: remove(archive) repo from filesystem
658 """
614 """
659 if not cur_user:
615 if not cur_user:
660 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
616 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
661 repo = self._get_repo(repo)
617 repo = self._get_repo(repo)
662 if repo:
618 if repo:
663 if forks == 'detach':
619 if forks == 'detach':
664 for r in repo.forks:
620 for r in repo.forks:
665 r.fork = None
621 r.fork = None
666 self.sa.add(r)
622 self.sa.add(r)
667 elif forks == 'delete':
623 elif forks == 'delete':
668 for r in repo.forks:
624 for r in repo.forks:
669 self.delete(r, forks='delete')
625 self.delete(r, forks='delete')
670 elif [f for f in repo.forks]:
626 elif [f for f in repo.forks]:
671 raise AttachedForksError()
627 raise AttachedForksError()
672
628
673 old_repo_dict = repo.get_dict()
629 old_repo_dict = repo.get_dict()
674 events.trigger(events.RepoPreDeleteEvent(repo))
630 events.trigger(events.RepoPreDeleteEvent(repo))
675 try:
631 try:
676 self.sa.delete(repo)
632 self.sa.delete(repo)
677 if fs_remove:
633 if fs_remove:
678 self._delete_filesystem_repo(repo)
634 self._delete_filesystem_repo(repo)
679 else:
635 else:
680 log.debug('skipping removal from filesystem')
636 log.debug('skipping removal from filesystem')
681 old_repo_dict.update({
637 old_repo_dict.update({
682 'deleted_by': cur_user,
638 'deleted_by': cur_user,
683 'deleted_on': time.time(),
639 'deleted_on': time.time(),
684 })
640 })
685 log_delete_repository(**old_repo_dict)
641 log_delete_repository(**old_repo_dict)
686 events.trigger(events.RepoDeleteEvent(repo))
642 events.trigger(events.RepoDeleteEvent(repo))
687 except Exception:
643 except Exception:
688 log.error(traceback.format_exc())
644 log.error(traceback.format_exc())
689 raise
645 raise
690
646
691 def grant_user_permission(self, repo, user, perm):
647 def grant_user_permission(self, repo, user, perm):
692 """
648 """
693 Grant permission for user on given repository, or update existing one
649 Grant permission for user on given repository, or update existing one
694 if found
650 if found
695
651
696 :param repo: Instance of Repository, repository_id, or repository name
652 :param repo: Instance of Repository, repository_id, or repository name
697 :param user: Instance of User, user_id or username
653 :param user: Instance of User, user_id or username
698 :param perm: Instance of Permission, or permission_name
654 :param perm: Instance of Permission, or permission_name
699 """
655 """
700 user = self._get_user(user)
656 user = self._get_user(user)
701 repo = self._get_repo(repo)
657 repo = self._get_repo(repo)
702 permission = self._get_perm(perm)
658 permission = self._get_perm(perm)
703
659
704 # check if we have that permission already
660 # check if we have that permission already
705 obj = self.sa.query(UserRepoToPerm) \
661 obj = self.sa.query(UserRepoToPerm) \
706 .filter(UserRepoToPerm.user == user) \
662 .filter(UserRepoToPerm.user == user) \
707 .filter(UserRepoToPerm.repository == repo) \
663 .filter(UserRepoToPerm.repository == repo) \
708 .scalar()
664 .scalar()
709 if obj is None:
665 if obj is None:
710 # create new !
666 # create new !
711 obj = UserRepoToPerm()
667 obj = UserRepoToPerm()
712 obj.repository = repo
668 obj.repository = repo
713 obj.user = user
669 obj.user = user
714 obj.permission = permission
670 obj.permission = permission
715 self.sa.add(obj)
671 self.sa.add(obj)
716 log.debug('Granted perm %s to %s on %s', perm, user, repo)
672 log.debug('Granted perm %s to %s on %s', perm, user, repo)
717 action_logger_generic(
673 action_logger_generic(
718 'granted permission: {} to user: {} on repo: {}'.format(
674 'granted permission: {} to user: {} on repo: {}'.format(
719 perm, user, repo), namespace='security.repo')
675 perm, user, repo), namespace='security.repo')
720 return obj
676 return obj
721
677
722 def revoke_user_permission(self, repo, user):
678 def revoke_user_permission(self, repo, user):
723 """
679 """
724 Revoke permission for user on given repository
680 Revoke permission for user on given repository
725
681
726 :param repo: Instance of Repository, repository_id, or repository name
682 :param repo: Instance of Repository, repository_id, or repository name
727 :param user: Instance of User, user_id or username
683 :param user: Instance of User, user_id or username
728 """
684 """
729
685
730 user = self._get_user(user)
686 user = self._get_user(user)
731 repo = self._get_repo(repo)
687 repo = self._get_repo(repo)
732
688
733 obj = self.sa.query(UserRepoToPerm) \
689 obj = self.sa.query(UserRepoToPerm) \
734 .filter(UserRepoToPerm.repository == repo) \
690 .filter(UserRepoToPerm.repository == repo) \
735 .filter(UserRepoToPerm.user == user) \
691 .filter(UserRepoToPerm.user == user) \
736 .scalar()
692 .scalar()
737 if obj:
693 if obj:
738 self.sa.delete(obj)
694 self.sa.delete(obj)
739 log.debug('Revoked perm on %s on %s', repo, user)
695 log.debug('Revoked perm on %s on %s', repo, user)
740 action_logger_generic(
696 action_logger_generic(
741 'revoked permission from user: {} on repo: {}'.format(
697 'revoked permission from user: {} on repo: {}'.format(
742 user, repo), namespace='security.repo')
698 user, repo), namespace='security.repo')
743
699
744 def grant_user_group_permission(self, repo, group_name, perm):
700 def grant_user_group_permission(self, repo, group_name, perm):
745 """
701 """
746 Grant permission for user group on given repository, or update
702 Grant permission for user group on given repository, or update
747 existing one if found
703 existing one if found
748
704
749 :param repo: Instance of Repository, repository_id, or repository name
705 :param repo: Instance of Repository, repository_id, or repository name
750 :param group_name: Instance of UserGroup, users_group_id,
706 :param group_name: Instance of UserGroup, users_group_id,
751 or user group name
707 or user group name
752 :param perm: Instance of Permission, or permission_name
708 :param perm: Instance of Permission, or permission_name
753 """
709 """
754 repo = self._get_repo(repo)
710 repo = self._get_repo(repo)
755 group_name = self._get_user_group(group_name)
711 group_name = self._get_user_group(group_name)
756 permission = self._get_perm(perm)
712 permission = self._get_perm(perm)
757
713
758 # check if we have that permission already
714 # check if we have that permission already
759 obj = self.sa.query(UserGroupRepoToPerm) \
715 obj = self.sa.query(UserGroupRepoToPerm) \
760 .filter(UserGroupRepoToPerm.users_group == group_name) \
716 .filter(UserGroupRepoToPerm.users_group == group_name) \
761 .filter(UserGroupRepoToPerm.repository == repo) \
717 .filter(UserGroupRepoToPerm.repository == repo) \
762 .scalar()
718 .scalar()
763
719
764 if obj is None:
720 if obj is None:
765 # create new
721 # create new
766 obj = UserGroupRepoToPerm()
722 obj = UserGroupRepoToPerm()
767
723
768 obj.repository = repo
724 obj.repository = repo
769 obj.users_group = group_name
725 obj.users_group = group_name
770 obj.permission = permission
726 obj.permission = permission
771 self.sa.add(obj)
727 self.sa.add(obj)
772 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
728 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
773 action_logger_generic(
729 action_logger_generic(
774 'granted permission: {} to usergroup: {} on repo: {}'.format(
730 'granted permission: {} to usergroup: {} on repo: {}'.format(
775 perm, group_name, repo), namespace='security.repo')
731 perm, group_name, repo), namespace='security.repo')
776
732
777 return obj
733 return obj
778
734
779 def revoke_user_group_permission(self, repo, group_name):
735 def revoke_user_group_permission(self, repo, group_name):
780 """
736 """
781 Revoke permission for user group on given repository
737 Revoke permission for user group on given repository
782
738
783 :param repo: Instance of Repository, repository_id, or repository name
739 :param repo: Instance of Repository, repository_id, or repository name
784 :param group_name: Instance of UserGroup, users_group_id,
740 :param group_name: Instance of UserGroup, users_group_id,
785 or user group name
741 or user group name
786 """
742 """
787 repo = self._get_repo(repo)
743 repo = self._get_repo(repo)
788 group_name = self._get_user_group(group_name)
744 group_name = self._get_user_group(group_name)
789
745
790 obj = self.sa.query(UserGroupRepoToPerm) \
746 obj = self.sa.query(UserGroupRepoToPerm) \
791 .filter(UserGroupRepoToPerm.repository == repo) \
747 .filter(UserGroupRepoToPerm.repository == repo) \
792 .filter(UserGroupRepoToPerm.users_group == group_name) \
748 .filter(UserGroupRepoToPerm.users_group == group_name) \
793 .scalar()
749 .scalar()
794 if obj:
750 if obj:
795 self.sa.delete(obj)
751 self.sa.delete(obj)
796 log.debug('Revoked perm to %s on %s', repo, group_name)
752 log.debug('Revoked perm to %s on %s', repo, group_name)
797 action_logger_generic(
753 action_logger_generic(
798 'revoked permission from usergroup: {} on repo: {}'.format(
754 'revoked permission from usergroup: {} on repo: {}'.format(
799 group_name, repo), namespace='security.repo')
755 group_name, repo), namespace='security.repo')
800
756
801 def delete_stats(self, repo_name):
757 def delete_stats(self, repo_name):
802 """
758 """
803 removes stats for given repo
759 removes stats for given repo
804
760
805 :param repo_name:
761 :param repo_name:
806 """
762 """
807 repo = self._get_repo(repo_name)
763 repo = self._get_repo(repo_name)
808 try:
764 try:
809 obj = self.sa.query(Statistics) \
765 obj = self.sa.query(Statistics) \
810 .filter(Statistics.repository == repo).scalar()
766 .filter(Statistics.repository == repo).scalar()
811 if obj:
767 if obj:
812 self.sa.delete(obj)
768 self.sa.delete(obj)
813 except Exception:
769 except Exception:
814 log.error(traceback.format_exc())
770 log.error(traceback.format_exc())
815 raise
771 raise
816
772
817 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
773 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
818 field_type='str', field_desc=''):
774 field_type='str', field_desc=''):
819
775
820 repo = self._get_repo(repo_name)
776 repo = self._get_repo(repo_name)
821
777
822 new_field = RepositoryField()
778 new_field = RepositoryField()
823 new_field.repository = repo
779 new_field.repository = repo
824 new_field.field_key = field_key
780 new_field.field_key = field_key
825 new_field.field_type = field_type # python type
781 new_field.field_type = field_type # python type
826 new_field.field_value = field_value
782 new_field.field_value = field_value
827 new_field.field_desc = field_desc
783 new_field.field_desc = field_desc
828 new_field.field_label = field_label
784 new_field.field_label = field_label
829 self.sa.add(new_field)
785 self.sa.add(new_field)
830 return new_field
786 return new_field
831
787
832 def delete_repo_field(self, repo_name, field_key):
788 def delete_repo_field(self, repo_name, field_key):
833 repo = self._get_repo(repo_name)
789 repo = self._get_repo(repo_name)
834 field = RepositoryField.get_by_key_name(field_key, repo)
790 field = RepositoryField.get_by_key_name(field_key, repo)
835 if field:
791 if field:
836 self.sa.delete(field)
792 self.sa.delete(field)
837
793
838 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
794 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
839 clone_uri=None, repo_store_location=None,
795 clone_uri=None, repo_store_location=None,
840 use_global_config=False):
796 use_global_config=False):
841 """
797 """
842 makes repository on filesystem. It's group aware means it'll create
798 makes repository on filesystem. It's group aware means it'll create
843 a repository within a group, and alter the paths accordingly of
799 a repository within a group, and alter the paths accordingly of
844 group location
800 group location
845
801
846 :param repo_name:
802 :param repo_name:
847 :param alias:
803 :param alias:
848 :param parent:
804 :param parent:
849 :param clone_uri:
805 :param clone_uri:
850 :param repo_store_location:
806 :param repo_store_location:
851 """
807 """
852 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
808 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
853 from rhodecode.model.scm import ScmModel
809 from rhodecode.model.scm import ScmModel
854
810
855 if Repository.NAME_SEP in repo_name:
811 if Repository.NAME_SEP in repo_name:
856 raise ValueError(
812 raise ValueError(
857 'repo_name must not contain groups got `%s`' % repo_name)
813 'repo_name must not contain groups got `%s`' % repo_name)
858
814
859 if isinstance(repo_group, RepoGroup):
815 if isinstance(repo_group, RepoGroup):
860 new_parent_path = os.sep.join(repo_group.full_path_splitted)
816 new_parent_path = os.sep.join(repo_group.full_path_splitted)
861 else:
817 else:
862 new_parent_path = repo_group or ''
818 new_parent_path = repo_group or ''
863
819
864 if repo_store_location:
820 if repo_store_location:
865 _paths = [repo_store_location]
821 _paths = [repo_store_location]
866 else:
822 else:
867 _paths = [self.repos_path, new_parent_path, repo_name]
823 _paths = [self.repos_path, new_parent_path, repo_name]
868 # we need to make it str for mercurial
824 # we need to make it str for mercurial
869 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
825 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
870
826
871 # check if this path is not a repository
827 # check if this path is not a repository
872 if is_valid_repo(repo_path, self.repos_path):
828 if is_valid_repo(repo_path, self.repos_path):
873 raise Exception('This path %s is a valid repository' % repo_path)
829 raise Exception('This path %s is a valid repository' % repo_path)
874
830
875 # check if this path is a group
831 # check if this path is a group
876 if is_valid_repo_group(repo_path, self.repos_path):
832 if is_valid_repo_group(repo_path, self.repos_path):
877 raise Exception('This path %s is a valid group' % repo_path)
833 raise Exception('This path %s is a valid group' % repo_path)
878
834
879 log.info('creating repo %s in %s from url: `%s`',
835 log.info('creating repo %s in %s from url: `%s`',
880 repo_name, safe_unicode(repo_path),
836 repo_name, safe_unicode(repo_path),
881 obfuscate_url_pw(clone_uri))
837 obfuscate_url_pw(clone_uri))
882
838
883 backend = get_backend(repo_type)
839 backend = get_backend(repo_type)
884
840
885 config_repo = None if use_global_config else repo_name
841 config_repo = None if use_global_config else repo_name
886 if config_repo and new_parent_path:
842 if config_repo and new_parent_path:
887 config_repo = Repository.NAME_SEP.join(
843 config_repo = Repository.NAME_SEP.join(
888 (new_parent_path, config_repo))
844 (new_parent_path, config_repo))
889 config = make_db_config(clear_session=False, repo=config_repo)
845 config = make_db_config(clear_session=False, repo=config_repo)
890 config.set('extensions', 'largefiles', '')
846 config.set('extensions', 'largefiles', '')
891
847
892 # patch and reset hooks section of UI config to not run any
848 # patch and reset hooks section of UI config to not run any
893 # hooks on creating remote repo
849 # hooks on creating remote repo
894 config.clear_section('hooks')
850 config.clear_section('hooks')
895
851
896 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
852 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
897 if repo_type == 'git':
853 if repo_type == 'git':
898 repo = backend(
854 repo = backend(
899 repo_path, config=config, create=True, src_url=clone_uri,
855 repo_path, config=config, create=True, src_url=clone_uri,
900 bare=True)
856 bare=True)
901 else:
857 else:
902 repo = backend(
858 repo = backend(
903 repo_path, config=config, create=True, src_url=clone_uri)
859 repo_path, config=config, create=True, src_url=clone_uri)
904
860
905 ScmModel().install_hooks(repo, repo_type=repo_type)
861 ScmModel().install_hooks(repo, repo_type=repo_type)
906
862
907 log.debug('Created repo %s with %s backend',
863 log.debug('Created repo %s with %s backend',
908 safe_unicode(repo_name), safe_unicode(repo_type))
864 safe_unicode(repo_name), safe_unicode(repo_type))
909 return repo
865 return repo
910
866
911 def _rename_filesystem_repo(self, old, new):
867 def _rename_filesystem_repo(self, old, new):
912 """
868 """
913 renames repository on filesystem
869 renames repository on filesystem
914
870
915 :param old: old name
871 :param old: old name
916 :param new: new name
872 :param new: new name
917 """
873 """
918 log.info('renaming repo from %s to %s', old, new)
874 log.info('renaming repo from %s to %s', old, new)
919
875
920 old_path = os.path.join(self.repos_path, old)
876 old_path = os.path.join(self.repos_path, old)
921 new_path = os.path.join(self.repos_path, new)
877 new_path = os.path.join(self.repos_path, new)
922 if os.path.isdir(new_path):
878 if os.path.isdir(new_path):
923 raise Exception(
879 raise Exception(
924 'Was trying to rename to already existing dir %s' % new_path
880 'Was trying to rename to already existing dir %s' % new_path
925 )
881 )
926 shutil.move(old_path, new_path)
882 shutil.move(old_path, new_path)
927
883
928 def _delete_filesystem_repo(self, repo):
884 def _delete_filesystem_repo(self, repo):
929 """
885 """
930 removes repo from filesystem, the removal is acctually made by
886 removes repo from filesystem, the removal is acctually made by
931 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
887 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
932 repository is no longer valid for rhodecode, can be undeleted later on
888 repository is no longer valid for rhodecode, can be undeleted later on
933 by reverting the renames on this repository
889 by reverting the renames on this repository
934
890
935 :param repo: repo object
891 :param repo: repo object
936 """
892 """
937 rm_path = os.path.join(self.repos_path, repo.repo_name)
893 rm_path = os.path.join(self.repos_path, repo.repo_name)
938 repo_group = repo.group
894 repo_group = repo.group
939 log.info("Removing repository %s", rm_path)
895 log.info("Removing repository %s", rm_path)
940 # disable hg/git internal that it doesn't get detected as repo
896 # disable hg/git internal that it doesn't get detected as repo
941 alias = repo.repo_type
897 alias = repo.repo_type
942
898
943 config = make_db_config(clear_session=False)
899 config = make_db_config(clear_session=False)
944 config.set('extensions', 'largefiles', '')
900 config.set('extensions', 'largefiles', '')
945 bare = getattr(repo.scm_instance(config=config), 'bare', False)
901 bare = getattr(repo.scm_instance(config=config), 'bare', False)
946
902
947 # skip this for bare git repos
903 # skip this for bare git repos
948 if not bare:
904 if not bare:
949 # disable VCS repo
905 # disable VCS repo
950 vcs_path = os.path.join(rm_path, '.%s' % alias)
906 vcs_path = os.path.join(rm_path, '.%s' % alias)
951 if os.path.exists(vcs_path):
907 if os.path.exists(vcs_path):
952 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
908 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
953
909
954 _now = datetime.now()
910 _now = datetime.now()
955 _ms = str(_now.microsecond).rjust(6, '0')
911 _ms = str(_now.microsecond).rjust(6, '0')
956 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
912 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
957 repo.just_name)
913 repo.just_name)
958 if repo_group:
914 if repo_group:
959 # if repository is in group, prefix the removal path with the group
915 # if repository is in group, prefix the removal path with the group
960 args = repo_group.full_path_splitted + [_d]
916 args = repo_group.full_path_splitted + [_d]
961 _d = os.path.join(*args)
917 _d = os.path.join(*args)
962
918
963 if os.path.isdir(rm_path):
919 if os.path.isdir(rm_path):
964 shutil.move(rm_path, os.path.join(self.repos_path, _d))
920 shutil.move(rm_path, os.path.join(self.repos_path, _d))
965
921
966
922
967 class ReadmeFinder:
923 class ReadmeFinder:
968 """
924 """
969 Utility which knows how to find a readme for a specific commit.
925 Utility which knows how to find a readme for a specific commit.
970
926
971 The main idea is that this is a configurable algorithm. When creating an
927 The main idea is that this is a configurable algorithm. When creating an
972 instance you can define parameters, currently only the `default_renderer`.
928 instance you can define parameters, currently only the `default_renderer`.
973 Based on this configuration the method :meth:`search` behaves slightly
929 Based on this configuration the method :meth:`search` behaves slightly
974 different.
930 different.
975 """
931 """
976
932
977 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
933 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
978 path_re = re.compile(r'^docs?', re.IGNORECASE)
934 path_re = re.compile(r'^docs?', re.IGNORECASE)
979
935
980 default_priorities = {
936 default_priorities = {
981 None: 0,
937 None: 0,
982 '.text': 2,
938 '.text': 2,
983 '.txt': 3,
939 '.txt': 3,
984 '.rst': 1,
940 '.rst': 1,
985 '.rest': 2,
941 '.rest': 2,
986 '.md': 1,
942 '.md': 1,
987 '.mkdn': 2,
943 '.mkdn': 2,
988 '.mdown': 3,
944 '.mdown': 3,
989 '.markdown': 4,
945 '.markdown': 4,
990 }
946 }
991
947
992 path_priority = {
948 path_priority = {
993 'doc': 0,
949 'doc': 0,
994 'docs': 1,
950 'docs': 1,
995 }
951 }
996
952
997 FALLBACK_PRIORITY = 99
953 FALLBACK_PRIORITY = 99
998
954
999 RENDERER_TO_EXTENSION = {
955 RENDERER_TO_EXTENSION = {
1000 'rst': ['.rst', '.rest'],
956 'rst': ['.rst', '.rest'],
1001 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
957 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1002 }
958 }
1003
959
1004 def __init__(self, default_renderer=None):
960 def __init__(self, default_renderer=None):
1005 self._default_renderer = default_renderer
961 self._default_renderer = default_renderer
1006 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
962 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1007 default_renderer, [])
963 default_renderer, [])
1008
964
1009 def search(self, commit, path='/'):
965 def search(self, commit, path='/'):
1010 """
966 """
1011 Find a readme in the given `commit`.
967 Find a readme in the given `commit`.
1012 """
968 """
1013 nodes = commit.get_nodes(path)
969 nodes = commit.get_nodes(path)
1014 matches = self._match_readmes(nodes)
970 matches = self._match_readmes(nodes)
1015 matches = self._sort_according_to_priority(matches)
971 matches = self._sort_according_to_priority(matches)
1016 if matches:
972 if matches:
1017 return matches[0].node
973 return matches[0].node
1018
974
1019 paths = self._match_paths(nodes)
975 paths = self._match_paths(nodes)
1020 paths = self._sort_paths_according_to_priority(paths)
976 paths = self._sort_paths_according_to_priority(paths)
1021 for path in paths:
977 for path in paths:
1022 match = self.search(commit, path=path)
978 match = self.search(commit, path=path)
1023 if match:
979 if match:
1024 return match
980 return match
1025
981
1026 return None
982 return None
1027
983
1028 def _match_readmes(self, nodes):
984 def _match_readmes(self, nodes):
1029 for node in nodes:
985 for node in nodes:
1030 if not node.is_file():
986 if not node.is_file():
1031 continue
987 continue
1032 path = node.path.rsplit('/', 1)[-1]
988 path = node.path.rsplit('/', 1)[-1]
1033 match = self.readme_re.match(path)
989 match = self.readme_re.match(path)
1034 if match:
990 if match:
1035 extension = match.group(1)
991 extension = match.group(1)
1036 yield ReadmeMatch(node, match, self._priority(extension))
992 yield ReadmeMatch(node, match, self._priority(extension))
1037
993
1038 def _match_paths(self, nodes):
994 def _match_paths(self, nodes):
1039 for node in nodes:
995 for node in nodes:
1040 if not node.is_dir():
996 if not node.is_dir():
1041 continue
997 continue
1042 match = self.path_re.match(node.path)
998 match = self.path_re.match(node.path)
1043 if match:
999 if match:
1044 yield node.path
1000 yield node.path
1045
1001
1046 def _priority(self, extension):
1002 def _priority(self, extension):
1047 renderer_priority = (
1003 renderer_priority = (
1048 0 if extension in self._renderer_extensions else 1)
1004 0 if extension in self._renderer_extensions else 1)
1049 extension_priority = self.default_priorities.get(
1005 extension_priority = self.default_priorities.get(
1050 extension, self.FALLBACK_PRIORITY)
1006 extension, self.FALLBACK_PRIORITY)
1051 return (renderer_priority, extension_priority)
1007 return (renderer_priority, extension_priority)
1052
1008
1053 def _sort_according_to_priority(self, matches):
1009 def _sort_according_to_priority(self, matches):
1054
1010
1055 def priority_and_path(match):
1011 def priority_and_path(match):
1056 return (match.priority, match.path)
1012 return (match.priority, match.path)
1057
1013
1058 return sorted(matches, key=priority_and_path)
1014 return sorted(matches, key=priority_and_path)
1059
1015
1060 def _sort_paths_according_to_priority(self, paths):
1016 def _sort_paths_according_to_priority(self, paths):
1061
1017
1062 def priority_and_path(path):
1018 def priority_and_path(path):
1063 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1019 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1064
1020
1065 return sorted(paths, key=priority_and_path)
1021 return sorted(paths, key=priority_and_path)
1066
1022
1067
1023
1068 class ReadmeMatch:
1024 class ReadmeMatch:
1069
1025
1070 def __init__(self, node, match, priority):
1026 def __init__(self, node, match, priority):
1071 self.node = node
1027 self.node = node
1072 self._match = match
1028 self._match = match
1073 self.priority = priority
1029 self.priority = priority
1074
1030
1075 @property
1031 @property
1076 def path(self):
1032 def path(self):
1077 return self.node.path
1033 return self.node.path
1078
1034
1079 def __repr__(self):
1035 def __repr__(self):
1080 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1036 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,559 +1,604 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 user group model for RhodeCode
23 user group model for RhodeCode
24 """
24 """
25
25
26
26
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from rhodecode.lib.utils2 import safe_str
30 from rhodecode.lib.utils2 import safe_str, safe_unicode
31 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
32 from rhodecode.model.db import UserGroupMember, UserGroup,\
32 from rhodecode.model.scm import UserGroupList
33 from rhodecode.model.db import true, func, UserGroupMember, UserGroup,\
33 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
34 UserGroupRepoToPerm, Permission, UserGroupToPerm, User, UserUserGroupToPerm,\
34 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
35 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm
35 from rhodecode.lib.exceptions import UserGroupAssignedException,\
36 from rhodecode.lib.exceptions import UserGroupAssignedException,\
36 RepoGroupAssignmentError
37 RepoGroupAssignmentError
37 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
38 from rhodecode.lib.utils2 import get_current_rhodecode_user, action_logger_generic
38
39
39 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
40
41
41
42
42 class UserGroupModel(BaseModel):
43 class UserGroupModel(BaseModel):
43
44
44 cls = UserGroup
45 cls = UserGroup
45
46
46 def _get_user_group(self, user_group):
47 def _get_user_group(self, user_group):
47 return self._get_instance(UserGroup, user_group,
48 return self._get_instance(UserGroup, user_group,
48 callback=UserGroup.get_by_group_name)
49 callback=UserGroup.get_by_group_name)
49
50
50 def _create_default_perms(self, user_group):
51 def _create_default_perms(self, user_group):
51 # create default permission
52 # create default permission
52 default_perm = 'usergroup.read'
53 default_perm = 'usergroup.read'
53 def_user = User.get_default_user()
54 def_user = User.get_default_user()
54 for p in def_user.user_perms:
55 for p in def_user.user_perms:
55 if p.permission.permission_name.startswith('usergroup.'):
56 if p.permission.permission_name.startswith('usergroup.'):
56 default_perm = p.permission.permission_name
57 default_perm = p.permission.permission_name
57 break
58 break
58
59
59 user_group_to_perm = UserUserGroupToPerm()
60 user_group_to_perm = UserUserGroupToPerm()
60 user_group_to_perm.permission = Permission.get_by_key(default_perm)
61 user_group_to_perm.permission = Permission.get_by_key(default_perm)
61
62
62 user_group_to_perm.user_group = user_group
63 user_group_to_perm.user_group = user_group
63 user_group_to_perm.user_id = def_user.user_id
64 user_group_to_perm.user_id = def_user.user_id
64 return user_group_to_perm
65 return user_group_to_perm
65
66
66 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
67 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
67 perm_deletions=None, check_perms=True, cur_user=None):
68 perm_deletions=None, check_perms=True, cur_user=None):
68 from rhodecode.lib.auth import HasUserGroupPermissionAny
69 from rhodecode.lib.auth import HasUserGroupPermissionAny
69 if not perm_additions:
70 if not perm_additions:
70 perm_additions = []
71 perm_additions = []
71 if not perm_updates:
72 if not perm_updates:
72 perm_updates = []
73 perm_updates = []
73 if not perm_deletions:
74 if not perm_deletions:
74 perm_deletions = []
75 perm_deletions = []
75
76
76 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
77 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
77
78
78 # update permissions
79 # update permissions
79 for member_id, perm, member_type in perm_updates:
80 for member_id, perm, member_type in perm_updates:
80 member_id = int(member_id)
81 member_id = int(member_id)
81 if member_type == 'user':
82 if member_type == 'user':
82 # this updates existing one
83 # this updates existing one
83 self.grant_user_permission(
84 self.grant_user_permission(
84 user_group=user_group, user=member_id, perm=perm
85 user_group=user_group, user=member_id, perm=perm
85 )
86 )
86 else:
87 else:
87 # check if we have permissions to alter this usergroup
88 # check if we have permissions to alter this usergroup
88 member_name = UserGroup.get(member_id).users_group_name
89 member_name = UserGroup.get(member_id).users_group_name
89 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
90 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
90 self.grant_user_group_permission(
91 self.grant_user_group_permission(
91 target_user_group=user_group, user_group=member_id, perm=perm
92 target_user_group=user_group, user_group=member_id, perm=perm
92 )
93 )
93
94
94 # set new permissions
95 # set new permissions
95 for member_id, perm, member_type in perm_additions:
96 for member_id, perm, member_type in perm_additions:
96 member_id = int(member_id)
97 member_id = int(member_id)
97 if member_type == 'user':
98 if member_type == 'user':
98 self.grant_user_permission(
99 self.grant_user_permission(
99 user_group=user_group, user=member_id, perm=perm
100 user_group=user_group, user=member_id, perm=perm
100 )
101 )
101 else:
102 else:
102 # check if we have permissions to alter this usergroup
103 # check if we have permissions to alter this usergroup
103 member_name = UserGroup.get(member_id).users_group_name
104 member_name = UserGroup.get(member_id).users_group_name
104 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
105 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
105 self.grant_user_group_permission(
106 self.grant_user_group_permission(
106 target_user_group=user_group, user_group=member_id, perm=perm
107 target_user_group=user_group, user_group=member_id, perm=perm
107 )
108 )
108
109
109 # delete permissions
110 # delete permissions
110 for member_id, perm, member_type in perm_deletions:
111 for member_id, perm, member_type in perm_deletions:
111 member_id = int(member_id)
112 member_id = int(member_id)
112 if member_type == 'user':
113 if member_type == 'user':
113 self.revoke_user_permission(user_group=user_group, user=member_id)
114 self.revoke_user_permission(user_group=user_group, user=member_id)
114 else:
115 else:
115 #check if we have permissions to alter this usergroup
116 #check if we have permissions to alter this usergroup
116 member_name = UserGroup.get(member_id).users_group_name
117 member_name = UserGroup.get(member_id).users_group_name
117 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
118 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
118 self.revoke_user_group_permission(
119 self.revoke_user_group_permission(
119 target_user_group=user_group, user_group=member_id
120 target_user_group=user_group, user_group=member_id
120 )
121 )
121
122
122 def get(self, user_group_id, cache=False):
123 def get(self, user_group_id, cache=False):
123 return UserGroup.get(user_group_id)
124 return UserGroup.get(user_group_id)
124
125
125 def get_group(self, user_group):
126 def get_group(self, user_group):
126 return self._get_user_group(user_group)
127 return self._get_user_group(user_group)
127
128
128 def get_by_name(self, name, cache=False, case_insensitive=False):
129 def get_by_name(self, name, cache=False, case_insensitive=False):
129 return UserGroup.get_by_group_name(name, cache, case_insensitive)
130 return UserGroup.get_by_group_name(name, cache, case_insensitive)
130
131
131 def create(self, name, description, owner, active=True, group_data=None):
132 def create(self, name, description, owner, active=True, group_data=None):
132 try:
133 try:
133 new_user_group = UserGroup()
134 new_user_group = UserGroup()
134 new_user_group.user = self._get_user(owner)
135 new_user_group.user = self._get_user(owner)
135 new_user_group.users_group_name = name
136 new_user_group.users_group_name = name
136 new_user_group.user_group_description = description
137 new_user_group.user_group_description = description
137 new_user_group.users_group_active = active
138 new_user_group.users_group_active = active
138 if group_data:
139 if group_data:
139 new_user_group.group_data = group_data
140 new_user_group.group_data = group_data
140 self.sa.add(new_user_group)
141 self.sa.add(new_user_group)
141 perm_obj = self._create_default_perms(new_user_group)
142 perm_obj = self._create_default_perms(new_user_group)
142 self.sa.add(perm_obj)
143 self.sa.add(perm_obj)
143
144
144 self.grant_user_permission(user_group=new_user_group,
145 self.grant_user_permission(user_group=new_user_group,
145 user=owner, perm='usergroup.admin')
146 user=owner, perm='usergroup.admin')
146
147
147 return new_user_group
148 return new_user_group
148 except Exception:
149 except Exception:
149 log.error(traceback.format_exc())
150 log.error(traceback.format_exc())
150 raise
151 raise
151
152
152 def _get_memberships_for_user_ids(self, user_group, user_id_list):
153 def _get_memberships_for_user_ids(self, user_group, user_id_list):
153 members = []
154 members = []
154 for user_id in user_id_list:
155 for user_id in user_id_list:
155 member = self._get_membership(user_group.users_group_id, user_id)
156 member = self._get_membership(user_group.users_group_id, user_id)
156 members.append(member)
157 members.append(member)
157 return members
158 return members
158
159
159 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
160 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
160 current_members = user_group.members or []
161 current_members = user_group.members or []
161 current_members_ids = [m.user.user_id for m in current_members]
162 current_members_ids = [m.user.user_id for m in current_members]
162
163
163 added_members = [
164 added_members = [
164 user_id for user_id in user_id_list
165 user_id for user_id in user_id_list
165 if user_id not in current_members_ids]
166 if user_id not in current_members_ids]
166 if user_id_list == []:
167 if user_id_list == []:
167 # all members were deleted
168 # all members were deleted
168 deleted_members = current_members_ids
169 deleted_members = current_members_ids
169 else:
170 else:
170 deleted_members = [
171 deleted_members = [
171 user_id for user_id in current_members_ids
172 user_id for user_id in current_members_ids
172 if user_id not in user_id_list]
173 if user_id not in user_id_list]
173
174
174 return (added_members, deleted_members)
175 return (added_members, deleted_members)
175
176
176 def _set_users_as_members(self, user_group, user_ids):
177 def _set_users_as_members(self, user_group, user_ids):
177 user_group.members = []
178 user_group.members = []
178 self.sa.flush()
179 self.sa.flush()
179 members = self._get_memberships_for_user_ids(
180 members = self._get_memberships_for_user_ids(
180 user_group, user_ids)
181 user_group, user_ids)
181 user_group.members = members
182 user_group.members = members
182 self.sa.add(user_group)
183 self.sa.add(user_group)
183
184
184 def _update_members_from_user_ids(self, user_group, user_ids):
185 def _update_members_from_user_ids(self, user_group, user_ids):
185 added, removed = self._get_added_and_removed_user_ids(
186 added, removed = self._get_added_and_removed_user_ids(
186 user_group, user_ids)
187 user_group, user_ids)
187 self._set_users_as_members(user_group, user_ids)
188 self._set_users_as_members(user_group, user_ids)
188 self._log_user_changes('added to', user_group, added)
189 self._log_user_changes('added to', user_group, added)
189 self._log_user_changes('removed from', user_group, removed)
190 self._log_user_changes('removed from', user_group, removed)
190
191
191 def _clean_members_data(self, members_data):
192 def _clean_members_data(self, members_data):
192 if not members_data:
193 if not members_data:
193 members_data = []
194 members_data = []
194
195
195 members = []
196 members = []
196 for user in members_data:
197 for user in members_data:
197 uid = int(user['member_user_id'])
198 uid = int(user['member_user_id'])
198 if uid not in members and user['type'] in ['new', 'existing']:
199 if uid not in members and user['type'] in ['new', 'existing']:
199 members.append(uid)
200 members.append(uid)
200 return members
201 return members
201
202
202 def update(self, user_group, form_data):
203 def update(self, user_group, form_data):
203 user_group = self._get_user_group(user_group)
204 user_group = self._get_user_group(user_group)
204 if 'users_group_name' in form_data:
205 if 'users_group_name' in form_data:
205 user_group.users_group_name = form_data['users_group_name']
206 user_group.users_group_name = form_data['users_group_name']
206 if 'users_group_active' in form_data:
207 if 'users_group_active' in form_data:
207 user_group.users_group_active = form_data['users_group_active']
208 user_group.users_group_active = form_data['users_group_active']
208 if 'user_group_description' in form_data:
209 if 'user_group_description' in form_data:
209 user_group.user_group_description = form_data[
210 user_group.user_group_description = form_data[
210 'user_group_description']
211 'user_group_description']
211
212
212 # handle owner change
213 # handle owner change
213 if 'user' in form_data:
214 if 'user' in form_data:
214 owner = form_data['user']
215 owner = form_data['user']
215 if isinstance(owner, basestring):
216 if isinstance(owner, basestring):
216 owner = User.get_by_username(form_data['user'])
217 owner = User.get_by_username(form_data['user'])
217
218
218 if not isinstance(owner, User):
219 if not isinstance(owner, User):
219 raise ValueError(
220 raise ValueError(
220 'invalid owner for user group: %s' % form_data['user'])
221 'invalid owner for user group: %s' % form_data['user'])
221
222
222 user_group.user = owner
223 user_group.user = owner
223
224
224 if 'users_group_members' in form_data:
225 if 'users_group_members' in form_data:
225 members_id_list = self._clean_members_data(
226 members_id_list = self._clean_members_data(
226 form_data['users_group_members'])
227 form_data['users_group_members'])
227 self._update_members_from_user_ids(user_group, members_id_list)
228 self._update_members_from_user_ids(user_group, members_id_list)
228
229
229 self.sa.add(user_group)
230 self.sa.add(user_group)
230
231
231 def delete(self, user_group, force=False):
232 def delete(self, user_group, force=False):
232 """
233 """
233 Deletes repository group, unless force flag is used
234 Deletes repository group, unless force flag is used
234 raises exception if there are members in that group, else deletes
235 raises exception if there are members in that group, else deletes
235 group and users
236 group and users
236
237
237 :param user_group:
238 :param user_group:
238 :param force:
239 :param force:
239 """
240 """
240 user_group = self._get_user_group(user_group)
241 user_group = self._get_user_group(user_group)
241 try:
242 try:
242 # check if this group is not assigned to repo
243 # check if this group is not assigned to repo
243 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
244 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
244 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
245 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
245 # check if this group is not assigned to repo
246 # check if this group is not assigned to repo
246 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
247 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
247 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
248 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
248
249
249 if (assigned_to_repo or assigned_to_repo_group) and not force:
250 if (assigned_to_repo or assigned_to_repo_group) and not force:
250 assigned = ','.join(map(safe_str,
251 assigned = ','.join(map(safe_str,
251 assigned_to_repo+assigned_to_repo_group))
252 assigned_to_repo+assigned_to_repo_group))
252
253
253 raise UserGroupAssignedException(
254 raise UserGroupAssignedException(
254 'UserGroup assigned to %s' % (assigned,))
255 'UserGroup assigned to %s' % (assigned,))
255 self.sa.delete(user_group)
256 self.sa.delete(user_group)
256 except Exception:
257 except Exception:
257 log.error(traceback.format_exc())
258 log.error(traceback.format_exc())
258 raise
259 raise
259
260
260 def _log_user_changes(self, action, user_group, user_or_users):
261 def _log_user_changes(self, action, user_group, user_or_users):
261 users = user_or_users
262 users = user_or_users
262 if not isinstance(users, (list, tuple)):
263 if not isinstance(users, (list, tuple)):
263 users = [users]
264 users = [users]
264 rhodecode_user = get_current_rhodecode_user()
265 rhodecode_user = get_current_rhodecode_user()
265 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
266 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
266 group_name = user_group.users_group_name
267 group_name = user_group.users_group_name
267
268
268 for user_or_user_id in users:
269 for user_or_user_id in users:
269 user = self._get_user(user_or_user_id)
270 user = self._get_user(user_or_user_id)
270 log_text = 'User {user} {action} {group}'.format(
271 log_text = 'User {user} {action} {group}'.format(
271 action=action, user=user.username, group=group_name)
272 action=action, user=user.username, group=group_name)
272 log.info('Logging action: {0} by {1} ip:{2}'.format(
273 log.info('Logging action: {0} by {1} ip:{2}'.format(
273 log_text, rhodecode_user, ipaddr))
274 log_text, rhodecode_user, ipaddr))
274
275
275 def _find_user_in_group(self, user, user_group):
276 def _find_user_in_group(self, user, user_group):
276 user_group_member = None
277 user_group_member = None
277 for m in user_group.members:
278 for m in user_group.members:
278 if m.user_id == user.user_id:
279 if m.user_id == user.user_id:
279 # Found this user's membership row
280 # Found this user's membership row
280 user_group_member = m
281 user_group_member = m
281 break
282 break
282
283
283 return user_group_member
284 return user_group_member
284
285
285 def _get_membership(self, user_group_id, user_id):
286 def _get_membership(self, user_group_id, user_id):
286 user_group_member = UserGroupMember(user_group_id, user_id)
287 user_group_member = UserGroupMember(user_group_id, user_id)
287 return user_group_member
288 return user_group_member
288
289
289 def add_user_to_group(self, user_group, user):
290 def add_user_to_group(self, user_group, user):
290 user_group = self._get_user_group(user_group)
291 user_group = self._get_user_group(user_group)
291 user = self._get_user(user)
292 user = self._get_user(user)
292 user_member = self._find_user_in_group(user, user_group)
293 user_member = self._find_user_in_group(user, user_group)
293 if user_member:
294 if user_member:
294 # user already in the group, skip
295 # user already in the group, skip
295 return True
296 return True
296
297
297 member = self._get_membership(
298 member = self._get_membership(
298 user_group.users_group_id, user.user_id)
299 user_group.users_group_id, user.user_id)
299 user_group.members.append(member)
300 user_group.members.append(member)
300
301
301 try:
302 try:
302 self.sa.add(member)
303 self.sa.add(member)
303 except Exception:
304 except Exception:
304 # what could go wrong here?
305 # what could go wrong here?
305 log.error(traceback.format_exc())
306 log.error(traceback.format_exc())
306 raise
307 raise
307
308
308 self._log_user_changes('added to', user_group, user)
309 self._log_user_changes('added to', user_group, user)
309 return member
310 return member
310
311
311 def remove_user_from_group(self, user_group, user):
312 def remove_user_from_group(self, user_group, user):
312 user_group = self._get_user_group(user_group)
313 user_group = self._get_user_group(user_group)
313 user = self._get_user(user)
314 user = self._get_user(user)
314 user_group_member = self._find_user_in_group(user, user_group)
315 user_group_member = self._find_user_in_group(user, user_group)
315
316
316 if not user_group_member:
317 if not user_group_member:
317 # User isn't in that group
318 # User isn't in that group
318 return False
319 return False
319
320
320 try:
321 try:
321 self.sa.delete(user_group_member)
322 self.sa.delete(user_group_member)
322 except Exception:
323 except Exception:
323 log.error(traceback.format_exc())
324 log.error(traceback.format_exc())
324 raise
325 raise
325
326
326 self._log_user_changes('removed from', user_group, user)
327 self._log_user_changes('removed from', user_group, user)
327 return True
328 return True
328
329
329 def has_perm(self, user_group, perm):
330 def has_perm(self, user_group, perm):
330 user_group = self._get_user_group(user_group)
331 user_group = self._get_user_group(user_group)
331 perm = self._get_perm(perm)
332 perm = self._get_perm(perm)
332
333
333 return UserGroupToPerm.query()\
334 return UserGroupToPerm.query()\
334 .filter(UserGroupToPerm.users_group == user_group)\
335 .filter(UserGroupToPerm.users_group == user_group)\
335 .filter(UserGroupToPerm.permission == perm).scalar() is not None
336 .filter(UserGroupToPerm.permission == perm).scalar() is not None
336
337
337 def grant_perm(self, user_group, perm):
338 def grant_perm(self, user_group, perm):
338 user_group = self._get_user_group(user_group)
339 user_group = self._get_user_group(user_group)
339 perm = self._get_perm(perm)
340 perm = self._get_perm(perm)
340
341
341 # if this permission is already granted skip it
342 # if this permission is already granted skip it
342 _perm = UserGroupToPerm.query()\
343 _perm = UserGroupToPerm.query()\
343 .filter(UserGroupToPerm.users_group == user_group)\
344 .filter(UserGroupToPerm.users_group == user_group)\
344 .filter(UserGroupToPerm.permission == perm)\
345 .filter(UserGroupToPerm.permission == perm)\
345 .scalar()
346 .scalar()
346 if _perm:
347 if _perm:
347 return
348 return
348
349
349 new = UserGroupToPerm()
350 new = UserGroupToPerm()
350 new.users_group = user_group
351 new.users_group = user_group
351 new.permission = perm
352 new.permission = perm
352 self.sa.add(new)
353 self.sa.add(new)
353 return new
354 return new
354
355
355 def revoke_perm(self, user_group, perm):
356 def revoke_perm(self, user_group, perm):
356 user_group = self._get_user_group(user_group)
357 user_group = self._get_user_group(user_group)
357 perm = self._get_perm(perm)
358 perm = self._get_perm(perm)
358
359
359 obj = UserGroupToPerm.query()\
360 obj = UserGroupToPerm.query()\
360 .filter(UserGroupToPerm.users_group == user_group)\
361 .filter(UserGroupToPerm.users_group == user_group)\
361 .filter(UserGroupToPerm.permission == perm).scalar()
362 .filter(UserGroupToPerm.permission == perm).scalar()
362 if obj:
363 if obj:
363 self.sa.delete(obj)
364 self.sa.delete(obj)
364
365
365 def grant_user_permission(self, user_group, user, perm):
366 def grant_user_permission(self, user_group, user, perm):
366 """
367 """
367 Grant permission for user on given user group, or update
368 Grant permission for user on given user group, or update
368 existing one if found
369 existing one if found
369
370
370 :param user_group: Instance of UserGroup, users_group_id,
371 :param user_group: Instance of UserGroup, users_group_id,
371 or users_group_name
372 or users_group_name
372 :param user: Instance of User, user_id or username
373 :param user: Instance of User, user_id or username
373 :param perm: Instance of Permission, or permission_name
374 :param perm: Instance of Permission, or permission_name
374 """
375 """
375
376
376 user_group = self._get_user_group(user_group)
377 user_group = self._get_user_group(user_group)
377 user = self._get_user(user)
378 user = self._get_user(user)
378 permission = self._get_perm(perm)
379 permission = self._get_perm(perm)
379
380
380 # check if we have that permission already
381 # check if we have that permission already
381 obj = self.sa.query(UserUserGroupToPerm)\
382 obj = self.sa.query(UserUserGroupToPerm)\
382 .filter(UserUserGroupToPerm.user == user)\
383 .filter(UserUserGroupToPerm.user == user)\
383 .filter(UserUserGroupToPerm.user_group == user_group)\
384 .filter(UserUserGroupToPerm.user_group == user_group)\
384 .scalar()
385 .scalar()
385 if obj is None:
386 if obj is None:
386 # create new !
387 # create new !
387 obj = UserUserGroupToPerm()
388 obj = UserUserGroupToPerm()
388 obj.user_group = user_group
389 obj.user_group = user_group
389 obj.user = user
390 obj.user = user
390 obj.permission = permission
391 obj.permission = permission
391 self.sa.add(obj)
392 self.sa.add(obj)
392 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
393 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
393 action_logger_generic(
394 action_logger_generic(
394 'granted permission: {} to user: {} on usergroup: {}'.format(
395 'granted permission: {} to user: {} on usergroup: {}'.format(
395 perm, user, user_group), namespace='security.usergroup')
396 perm, user, user_group), namespace='security.usergroup')
396
397
397 return obj
398 return obj
398
399
399 def revoke_user_permission(self, user_group, user):
400 def revoke_user_permission(self, user_group, user):
400 """
401 """
401 Revoke permission for user on given user group
402 Revoke permission for user on given user group
402
403
403 :param user_group: Instance of UserGroup, users_group_id,
404 :param user_group: Instance of UserGroup, users_group_id,
404 or users_group name
405 or users_group name
405 :param user: Instance of User, user_id or username
406 :param user: Instance of User, user_id or username
406 """
407 """
407
408
408 user_group = self._get_user_group(user_group)
409 user_group = self._get_user_group(user_group)
409 user = self._get_user(user)
410 user = self._get_user(user)
410
411
411 obj = self.sa.query(UserUserGroupToPerm)\
412 obj = self.sa.query(UserUserGroupToPerm)\
412 .filter(UserUserGroupToPerm.user == user)\
413 .filter(UserUserGroupToPerm.user == user)\
413 .filter(UserUserGroupToPerm.user_group == user_group)\
414 .filter(UserUserGroupToPerm.user_group == user_group)\
414 .scalar()
415 .scalar()
415 if obj:
416 if obj:
416 self.sa.delete(obj)
417 self.sa.delete(obj)
417 log.debug('Revoked perm on %s on %s', user_group, user)
418 log.debug('Revoked perm on %s on %s', user_group, user)
418 action_logger_generic(
419 action_logger_generic(
419 'revoked permission from user: {} on usergroup: {}'.format(
420 'revoked permission from user: {} on usergroup: {}'.format(
420 user, user_group), namespace='security.usergroup')
421 user, user_group), namespace='security.usergroup')
421
422
422 def grant_user_group_permission(self, target_user_group, user_group, perm):
423 def grant_user_group_permission(self, target_user_group, user_group, perm):
423 """
424 """
424 Grant user group permission for given target_user_group
425 Grant user group permission for given target_user_group
425
426
426 :param target_user_group:
427 :param target_user_group:
427 :param user_group:
428 :param user_group:
428 :param perm:
429 :param perm:
429 """
430 """
430 target_user_group = self._get_user_group(target_user_group)
431 target_user_group = self._get_user_group(target_user_group)
431 user_group = self._get_user_group(user_group)
432 user_group = self._get_user_group(user_group)
432 permission = self._get_perm(perm)
433 permission = self._get_perm(perm)
433 # forbid assigning same user group to itself
434 # forbid assigning same user group to itself
434 if target_user_group == user_group:
435 if target_user_group == user_group:
435 raise RepoGroupAssignmentError('target repo:%s cannot be '
436 raise RepoGroupAssignmentError('target repo:%s cannot be '
436 'assigned to itself' % target_user_group)
437 'assigned to itself' % target_user_group)
437
438
438 # check if we have that permission already
439 # check if we have that permission already
439 obj = self.sa.query(UserGroupUserGroupToPerm)\
440 obj = self.sa.query(UserGroupUserGroupToPerm)\
440 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
441 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
441 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
442 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
442 .scalar()
443 .scalar()
443 if obj is None:
444 if obj is None:
444 # create new !
445 # create new !
445 obj = UserGroupUserGroupToPerm()
446 obj = UserGroupUserGroupToPerm()
446 obj.user_group = user_group
447 obj.user_group = user_group
447 obj.target_user_group = target_user_group
448 obj.target_user_group = target_user_group
448 obj.permission = permission
449 obj.permission = permission
449 self.sa.add(obj)
450 self.sa.add(obj)
450 log.debug(
451 log.debug(
451 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
452 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
452 action_logger_generic(
453 action_logger_generic(
453 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
454 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
454 perm, user_group, target_user_group),
455 perm, user_group, target_user_group),
455 namespace='security.usergroup')
456 namespace='security.usergroup')
456
457
457 return obj
458 return obj
458
459
459 def revoke_user_group_permission(self, target_user_group, user_group):
460 def revoke_user_group_permission(self, target_user_group, user_group):
460 """
461 """
461 Revoke user group permission for given target_user_group
462 Revoke user group permission for given target_user_group
462
463
463 :param target_user_group:
464 :param target_user_group:
464 :param user_group:
465 :param user_group:
465 """
466 """
466 target_user_group = self._get_user_group(target_user_group)
467 target_user_group = self._get_user_group(target_user_group)
467 user_group = self._get_user_group(user_group)
468 user_group = self._get_user_group(user_group)
468
469
469 obj = self.sa.query(UserGroupUserGroupToPerm)\
470 obj = self.sa.query(UserGroupUserGroupToPerm)\
470 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
471 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
471 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
472 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
472 .scalar()
473 .scalar()
473 if obj:
474 if obj:
474 self.sa.delete(obj)
475 self.sa.delete(obj)
475 log.debug(
476 log.debug(
476 'Revoked perm on %s on %s', target_user_group, user_group)
477 'Revoked perm on %s on %s', target_user_group, user_group)
477 action_logger_generic(
478 action_logger_generic(
478 'revoked permission from usergroup: {} on usergroup: {}'.format(
479 'revoked permission from usergroup: {} on usergroup: {}'.format(
479 user_group, target_user_group),
480 user_group, target_user_group),
480 namespace='security.repogroup')
481 namespace='security.repogroup')
481
482
482 def enforce_groups(self, user, groups, extern_type=None):
483 def enforce_groups(self, user, groups, extern_type=None):
483 user = self._get_user(user)
484 user = self._get_user(user)
484 log.debug('Enforcing groups %s on user %s', groups, user)
485 log.debug('Enforcing groups %s on user %s', groups, user)
485 current_groups = user.group_member
486 current_groups = user.group_member
486 # find the external created groups
487 # find the external created groups
487 externals = [x.users_group for x in current_groups
488 externals = [x.users_group for x in current_groups
488 if 'extern_type' in x.users_group.group_data]
489 if 'extern_type' in x.users_group.group_data]
489
490
490 # calculate from what groups user should be removed
491 # calculate from what groups user should be removed
491 # externals that are not in groups
492 # externals that are not in groups
492 for gr in externals:
493 for gr in externals:
493 if gr.users_group_name not in groups:
494 if gr.users_group_name not in groups:
494 log.debug('Removing user %s from user group %s', user, gr)
495 log.debug('Removing user %s from user group %s', user, gr)
495 self.remove_user_from_group(gr, user)
496 self.remove_user_from_group(gr, user)
496
497
497 # now we calculate in which groups user should be == groups params
498 # now we calculate in which groups user should be == groups params
498 owner = User.get_first_super_admin().username
499 owner = User.get_first_super_admin().username
499 for gr in set(groups):
500 for gr in set(groups):
500 existing_group = UserGroup.get_by_group_name(gr)
501 existing_group = UserGroup.get_by_group_name(gr)
501 if not existing_group:
502 if not existing_group:
502 desc = 'Automatically created from plugin:%s' % extern_type
503 desc = 'Automatically created from plugin:%s' % extern_type
503 # we use first admin account to set the owner of the group
504 # we use first admin account to set the owner of the group
504 existing_group = UserGroupModel().create(
505 existing_group = UserGroupModel().create(
505 gr, desc, owner, group_data={'extern_type': extern_type})
506 gr, desc, owner, group_data={'extern_type': extern_type})
506
507
507 # we can only add users to special groups created via plugins
508 # we can only add users to special groups created via plugins
508 managed = 'extern_type' in existing_group.group_data
509 managed = 'extern_type' in existing_group.group_data
509 if managed:
510 if managed:
510 log.debug('Adding user %s to user group %s', user, gr)
511 log.debug('Adding user %s to user group %s', user, gr)
511 UserGroupModel().add_user_to_group(existing_group, user)
512 UserGroupModel().add_user_to_group(existing_group, user)
512 else:
513 else:
513 log.debug('Skipping addition to group %s since it is '
514 log.debug('Skipping addition to group %s since it is '
514 'not set to be automatically synchronized' % gr)
515 'not set to be automatically synchronized' % gr)
515
516
516 def change_groups(self, user, groups):
517 def change_groups(self, user, groups):
517 """
518 """
518 This method changes user group assignment
519 This method changes user group assignment
519 :param user: User
520 :param user: User
520 :param groups: array of UserGroupModel
521 :param groups: array of UserGroupModel
521 :return:
522 :return:
522 """
523 """
523 user = self._get_user(user)
524 user = self._get_user(user)
524 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
525 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
525 current_groups = user.group_member
526 current_groups = user.group_member
526 current_groups = [x.users_group for x in current_groups]
527 current_groups = [x.users_group for x in current_groups]
527
528
528 # calculate from what groups user should be removed/add
529 # calculate from what groups user should be removed/add
529 groups = set(groups)
530 groups = set(groups)
530 current_groups = set(current_groups)
531 current_groups = set(current_groups)
531
532
532 groups_to_remove = current_groups - groups
533 groups_to_remove = current_groups - groups
533 groups_to_add = groups - current_groups
534 groups_to_add = groups - current_groups
534
535
535 for gr in groups_to_remove:
536 for gr in groups_to_remove:
536 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
537 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
537 self.remove_user_from_group(gr.users_group_name, user.username)
538 self.remove_user_from_group(gr.users_group_name, user.username)
538 for gr in groups_to_add:
539 for gr in groups_to_add:
539 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
540 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
540 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
541 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
541
542
543 def get_user_groups(self, name_contains=None, limit=20, only_active=True,
544 expand_groups=False):
545 import rhodecode.lib.helpers as h
546
547 query = self.sa.query(UserGroup)
548 if only_active:
549 query = query.filter(UserGroup.users_group_active == true())
550
551 if name_contains:
552 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
553 query = query.filter(
554 UserGroup.users_group_name.ilike(ilike_expression))\
555 .order_by(func.length(UserGroup.users_group_name))\
556 .order_by(UserGroup.users_group_name)
557
558 query = query.limit(limit)
559 user_groups = query.all()
560 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
561 user_groups = UserGroupList(user_groups, perm_set=perm_set)
562
563 _groups = [
564 {
565 'id': group.users_group_id,
566 # TODO: marcink figure out a way to generate the url for the
567 # icon
568 'icon_link': '',
569 'value_display': 'Group: %s (%d members)' % (
570 group.users_group_name, len(group.members),),
571 'value': group.users_group_name,
572 'description': group.user_group_description,
573 'owner': group.user.username,
574
575 'owner_icon': h.gravatar_url(group.user.email, 30),
576 'value_display_owner': h.person(group.user.email),
577
578 'value_type': 'user_group',
579 'active': group.users_group_active,
580 }
581 for group in user_groups
582 ]
583 return _groups
584
542 @staticmethod
585 @staticmethod
543 def get_user_groups_as_dict(user_group):
586 def get_user_groups_as_dict(user_group):
544 import rhodecode.lib.helpers as h
587 import rhodecode.lib.helpers as h
545
588
546 data = {
589 data = {
547 'users_group_id': user_group.users_group_id,
590 'users_group_id': user_group.users_group_id,
548 'group_name': user_group.users_group_name,
591 'group_name': user_group.users_group_name,
549 'group_description': user_group.user_group_description,
592 'group_description': user_group.user_group_description,
550 'active': user_group.users_group_active,
593 'active': user_group.users_group_active,
551 "owner": user_group.user.username,
594 "owner": user_group.user.username,
552 'owner_icon': h.gravatar_url(user_group.user.email, 30),
595 'owner_icon': h.gravatar_url(user_group.user.email, 30),
553 "owner_data": {'owner': user_group.user.username, 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
596 "owner_data": {
597 'owner': user_group.user.username,
598 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
554 }
599 }
555 return data
600 return data
556
601
557
602
558
603
559
604
@@ -1,310 +1,252 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import tempfile
21 import tempfile
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.lib.exceptions import AttachedForksError
26 from rhodecode.lib.exceptions import AttachedForksError
27 from rhodecode.lib.utils import make_db_config
27 from rhodecode.lib.utils import make_db_config
28 from rhodecode.model.db import Repository
28 from rhodecode.model.db import Repository
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.scm import ScmModel
31 from rhodecode.model.scm import ScmModel
32 from rhodecode.lib.utils2 import safe_unicode
33
32
34
33
35 class TestRepoModel:
34 class TestRepoModel(object):
36
35
37 def test_remove_repo(self, backend):
36 def test_remove_repo(self, backend):
38 repo = backend.create_repo()
37 repo = backend.create_repo()
39 Session().commit()
38 Session().commit()
40 RepoModel().delete(repo=repo)
39 RepoModel().delete(repo=repo)
41 Session().commit()
40 Session().commit()
42
41
43 repos = ScmModel().repo_scan()
42 repos = ScmModel().repo_scan()
44
43
45 assert Repository.get_by_repo_name(repo_name=backend.repo_name) is None
44 assert Repository.get_by_repo_name(repo_name=backend.repo_name) is None
46 assert repo.repo_name not in repos
45 assert repo.repo_name not in repos
47
46
48 def test_remove_repo_raises_exc_when_attached_forks(self, backend):
47 def test_remove_repo_raises_exc_when_attached_forks(self, backend):
49 repo = backend.create_repo()
48 repo = backend.create_repo()
50 Session().commit()
49 Session().commit()
51 backend.create_fork()
50 backend.create_fork()
52 Session().commit()
51 Session().commit()
53
52
54 with pytest.raises(AttachedForksError):
53 with pytest.raises(AttachedForksError):
55 RepoModel().delete(repo=repo)
54 RepoModel().delete(repo=repo)
56
55
57 def test_remove_repo_delete_forks(self, backend):
56 def test_remove_repo_delete_forks(self, backend):
58 repo = backend.create_repo()
57 repo = backend.create_repo()
59 Session().commit()
58 Session().commit()
60
59
61 fork = backend.create_fork()
60 fork = backend.create_fork()
62 Session().commit()
61 Session().commit()
63
62
64 fork_of_fork = backend.create_fork()
63 fork_of_fork = backend.create_fork()
65 Session().commit()
64 Session().commit()
66
65
67 RepoModel().delete(repo=repo, forks='delete')
66 RepoModel().delete(repo=repo, forks='delete')
68 Session().commit()
67 Session().commit()
69
68
70 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
69 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
71 assert Repository.get_by_repo_name(repo_name=fork.repo_name) is None
70 assert Repository.get_by_repo_name(repo_name=fork.repo_name) is None
72 assert (
71 assert (
73 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
72 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
74 is None)
73 is None)
75
74
76 def test_remove_repo_detach_forks(self, backend):
75 def test_remove_repo_detach_forks(self, backend):
77 repo = backend.create_repo()
76 repo = backend.create_repo()
78 Session().commit()
77 Session().commit()
79
78
80 fork = backend.create_fork()
79 fork = backend.create_fork()
81 Session().commit()
80 Session().commit()
82
81
83 fork_of_fork = backend.create_fork()
82 fork_of_fork = backend.create_fork()
84 Session().commit()
83 Session().commit()
85
84
86 RepoModel().delete(repo=repo, forks='detach')
85 RepoModel().delete(repo=repo, forks='detach')
87 Session().commit()
86 Session().commit()
88
87
89 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
88 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
90 assert (
89 assert (
91 Repository.get_by_repo_name(repo_name=fork.repo_name) is not None)
90 Repository.get_by_repo_name(repo_name=fork.repo_name) is not None)
92 assert (
91 assert (
93 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
92 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
94 is not None)
93 is not None)
95
94
96 @pytest.mark.parametrize("filename, expected", [
95 @pytest.mark.parametrize("filename, expected", [
97 ("README", True),
96 ("README", True),
98 ("README.rst", False),
97 ("README.rst", False),
99 ])
98 ])
100 def test_filenode_is_link(self, vcsbackend, filename, expected):
99 def test_filenode_is_link(self, vcsbackend, filename, expected):
101 repo = vcsbackend.repo
100 repo = vcsbackend.repo
102 assert repo.get_commit().is_link(filename) is expected
101 assert repo.get_commit().is_link(filename) is expected
103
102
104 def test_get_commit(self, backend):
103 def test_get_commit(self, backend):
105 backend.repo.get_commit()
104 backend.repo.get_commit()
106
105
107 def test_get_changeset_is_deprecated(self, backend):
106 def test_get_changeset_is_deprecated(self, backend):
108 repo = backend.repo
107 repo = backend.repo
109 pytest.deprecated_call(repo.get_changeset)
108 pytest.deprecated_call(repo.get_changeset)
110
109
111 def test_clone_url_encrypted_value(self, backend):
110 def test_clone_url_encrypted_value(self, backend):
112 repo = backend.create_repo()
111 repo = backend.create_repo()
113 Session().commit()
112 Session().commit()
114
113
115 repo.clone_url = 'https://marcink:qweqwe@code.rhodecode.com'
114 repo.clone_url = 'https://marcink:qweqwe@code.rhodecode.com'
116 Session().add(repo)
115 Session().add(repo)
117 Session().commit()
116 Session().commit()
118
117
119 assert repo.clone_url == 'https://marcink:qweqwe@code.rhodecode.com'
118 assert repo.clone_url == 'https://marcink:qweqwe@code.rhodecode.com'
120
119
121 @pytest.mark.backends("git", "svn")
120 @pytest.mark.backends("git", "svn")
122 def test_create_filesystem_repo_installs_hooks(self, tmpdir, backend):
121 def test_create_filesystem_repo_installs_hooks(self, tmpdir, backend):
123 hook_methods = {
122 hook_methods = {
124 'git': 'install_git_hook',
123 'git': 'install_git_hook',
125 'svn': 'install_svn_hooks'
124 'svn': 'install_svn_hooks'
126 }
125 }
127 repo = backend.create_repo()
126 repo = backend.create_repo()
128 repo_name = repo.repo_name
127 repo_name = repo.repo_name
129 model = RepoModel()
128 model = RepoModel()
130 repo_location = tempfile.mkdtemp()
129 repo_location = tempfile.mkdtemp()
131 model.repos_path = repo_location
130 model.repos_path = repo_location
132 method = hook_methods[backend.alias]
131 method = hook_methods[backend.alias]
133 with mock.patch.object(ScmModel, method) as hooks_mock:
132 with mock.patch.object(ScmModel, method) as hooks_mock:
134 model._create_filesystem_repo(
133 model._create_filesystem_repo(
135 repo_name, backend.alias, repo_group='', clone_uri=None)
134 repo_name, backend.alias, repo_group='', clone_uri=None)
136 assert hooks_mock.call_count == 1
135 assert hooks_mock.call_count == 1
137 hook_args, hook_kwargs = hooks_mock.call_args
136 hook_args, hook_kwargs = hooks_mock.call_args
138 assert hook_args[0].name == repo_name
137 assert hook_args[0].name == repo_name
139
138
140 @pytest.mark.parametrize("use_global_config, repo_name_passed", [
139 @pytest.mark.parametrize("use_global_config, repo_name_passed", [
141 (True, False),
140 (True, False),
142 (False, True)
141 (False, True)
143 ])
142 ])
144 def test_per_repo_config_is_generated_during_filesystem_repo_creation(
143 def test_per_repo_config_is_generated_during_filesystem_repo_creation(
145 self, tmpdir, backend, use_global_config, repo_name_passed):
144 self, tmpdir, backend, use_global_config, repo_name_passed):
146 repo_name = 'test-{}-repo-{}'.format(backend.alias, use_global_config)
145 repo_name = 'test-{}-repo-{}'.format(backend.alias, use_global_config)
147 config = make_db_config()
146 config = make_db_config()
148 model = RepoModel()
147 model = RepoModel()
149 with mock.patch('rhodecode.model.repo.make_db_config') as config_mock:
148 with mock.patch('rhodecode.model.repo.make_db_config') as config_mock:
150 config_mock.return_value = config
149 config_mock.return_value = config
151 model._create_filesystem_repo(
150 model._create_filesystem_repo(
152 repo_name, backend.alias, repo_group='', clone_uri=None,
151 repo_name, backend.alias, repo_group='', clone_uri=None,
153 use_global_config=use_global_config)
152 use_global_config=use_global_config)
154 expected_repo_name = repo_name if repo_name_passed else None
153 expected_repo_name = repo_name if repo_name_passed else None
155 expected_call = mock.call(clear_session=False, repo=expected_repo_name)
154 expected_call = mock.call(clear_session=False, repo=expected_repo_name)
156 assert expected_call in config_mock.call_args_list
155 assert expected_call in config_mock.call_args_list
157
156
158 def test_update_commit_cache_with_config(serf, backend):
157 def test_update_commit_cache_with_config(serf, backend):
159 repo = backend.create_repo()
158 repo = backend.create_repo()
160 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm:
159 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm:
161 scm_instance = mock.Mock()
160 scm_instance = mock.Mock()
162 scm_instance.get_commit.return_value = {
161 scm_instance.get_commit.return_value = {
163 'raw_id': 40*'0',
162 'raw_id': 40*'0',
164 'revision': 1
163 'revision': 1
165 }
164 }
166 scm.return_value = scm_instance
165 scm.return_value = scm_instance
167 repo.update_commit_cache()
166 repo.update_commit_cache()
168 scm.assert_called_with(cache=False, config=None)
167 scm.assert_called_with(cache=False, config=None)
169 config = {'test': 'config'}
168 config = {'test': 'config'}
170 repo.update_commit_cache(config=config)
169 repo.update_commit_cache(config=config)
171 scm.assert_called_with(
170 scm.assert_called_with(
172 cache=False, config=config)
171 cache=False, config=config)
173
172
174
173
175 class TestGetUsers(object):
174 class TestGetUsers(object):
176 def test_returns_active_users(self, backend, user_util):
175 def test_returns_active_users(self, backend, user_util):
177 for i in range(4):
176 for i in range(4):
178 is_active = i % 2 == 0
177 is_active = i % 2 == 0
179 user_util.create_user(active=is_active, lastname='Fake user')
178 user_util.create_user(active=is_active, lastname='Fake user')
180
179
181 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
180 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
182 users = RepoModel().get_users()
181 users = RepoModel().get_users()
183 fake_users = [u for u in users if u['last_name'] == 'Fake user']
182 fake_users = [u for u in users if u['last_name'] == 'Fake user']
184 assert len(fake_users) == 2
183 assert len(fake_users) == 2
185
184
186 expected_keys = (
185 expected_keys = (
187 'id', 'first_name', 'last_name', 'username', 'icon_link',
186 'id', 'first_name', 'last_name', 'username', 'icon_link',
188 'value_display', 'value', 'value_type')
187 'value_display', 'value', 'value_type')
189 for user in users:
188 for user in users:
190 assert user['value_type'] is 'user'
189 assert user['value_type'] is 'user'
191 for key in expected_keys:
190 for key in expected_keys:
192 assert key in user
191 assert key in user
193
192
194 def test_returns_user_filtered_by_last_name(self, backend, user_util):
193 def test_returns_user_filtered_by_last_name(self, backend, user_util):
195 keywords = ('aBc', u'ΓΌnicode')
194 keywords = ('aBc', u'ΓΌnicode')
196 for keyword in keywords:
195 for keyword in keywords:
197 for i in range(2):
196 for i in range(2):
198 user_util.create_user(
197 user_util.create_user(
199 active=True, lastname=u'Fake {} user'.format(keyword))
198 active=True, lastname=u'Fake {} user'.format(keyword))
200
199
201 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
200 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
202 keyword = keywords[1].lower()
201 keyword = keywords[1].lower()
203 users = RepoModel().get_users(name_contains=keyword)
202 users = RepoModel().get_users(name_contains=keyword)
204
203
205 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
204 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
206 assert len(fake_users) == 2
205 assert len(fake_users) == 2
207 for user in fake_users:
206 for user in fake_users:
208 assert user['last_name'] == safe_unicode('Fake ΓΌnicode user')
207 assert user['last_name'] == safe_unicode('Fake ΓΌnicode user')
209
208
210 def test_returns_user_filtered_by_first_name(self, backend, user_util):
209 def test_returns_user_filtered_by_first_name(self, backend, user_util):
211 created_users = []
210 created_users = []
212 keywords = ('aBc', u'ΓΌnicode')
211 keywords = ('aBc', u'ΓΌnicode')
213 for keyword in keywords:
212 for keyword in keywords:
214 for i in range(2):
213 for i in range(2):
215 created_users.append(user_util.create_user(
214 created_users.append(user_util.create_user(
216 active=True, lastname='Fake user',
215 active=True, lastname='Fake user',
217 firstname=u'Fake {} user'.format(keyword)))
216 firstname=u'Fake {} user'.format(keyword)))
218
217
219 keyword = keywords[1].lower()
218 keyword = keywords[1].lower()
220 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
219 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
221 users = RepoModel().get_users(name_contains=keyword)
220 users = RepoModel().get_users(name_contains=keyword)
222
221
223 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
222 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
224 assert len(fake_users) == 2
223 assert len(fake_users) == 2
225 for user in fake_users:
224 for user in fake_users:
226 assert user['first_name'] == safe_unicode('Fake ΓΌnicode user')
225 assert user['first_name'] == safe_unicode('Fake ΓΌnicode user')
227
226
228 def test_returns_user_filtered_by_username(self, backend, user_util):
227 def test_returns_user_filtered_by_username(self, backend, user_util):
229 created_users = []
228 created_users = []
230 for i in range(5):
229 for i in range(5):
231 created_users.append(user_util.create_user(
230 created_users.append(user_util.create_user(
232 active=True, lastname='Fake user'))
231 active=True, lastname='Fake user'))
233
232
234 user_filter = created_users[-1].username[-2:]
233 user_filter = created_users[-1].username[-2:]
235 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
234 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
236 users = RepoModel().get_users(name_contains=user_filter)
235 users = RepoModel().get_users(name_contains=user_filter)
237
236
238 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
237 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
239 assert len(fake_users) == 1
238 assert len(fake_users) == 1
240 assert fake_users[0]['username'] == created_users[-1].username
239 assert fake_users[0]['username'] == created_users[-1].username
241
240
242 def test_returns_limited_user_list(self, backend, user_util):
241 def test_returns_limited_user_list(self, backend, user_util):
243 created_users = []
242 created_users = []
244 for i in range(5):
243 for i in range(5):
245 created_users.append(user_util.create_user(
244 created_users.append(user_util.create_user(
246 active=True, lastname='Fake user'))
245 active=True, lastname='Fake user'))
247
246
248 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
247 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
249 users = RepoModel().get_users(name_contains='Fake', limit=3)
248 users = RepoModel().get_users(name_contains='Fake', limit=3)
250
249
251 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
250 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
252 assert len(fake_users) == 3
251 assert len(fake_users) == 3
253
252
254
255 class TestGetUserGroups(object):
256 def test_returns_filtered_list(self, backend, user_util):
257 created_groups = []
258 for i in range(4):
259 created_groups.append(
260 user_util.create_user_group(users_group_active=True))
261
262 group_filter = created_groups[-1].users_group_name[-2:]
263 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
264 with self._patch_user_group_list():
265 groups = RepoModel().get_user_groups(group_filter)
266
267 fake_groups = [
268 u for u in groups if u['value'].startswith('test_returns')]
269 assert len(fake_groups) == 1
270 assert fake_groups[0]['value'] == created_groups[-1].users_group_name
271 assert fake_groups[0]['value_display'].startswith(
272 'Group: test_returns')
273
274 def test_returns_limited_list(self, backend, user_util):
275 created_groups = []
276 for i in range(3):
277 created_groups.append(
278 user_util.create_user_group(users_group_active=True))
279 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
280 with self._patch_user_group_list():
281 groups = RepoModel().get_user_groups('test_returns')
282
283 fake_groups = [
284 u for u in groups if u['value'].startswith('test_returns')]
285 assert len(fake_groups) == 3
286
287 def test_returns_active_user_groups(self, backend, user_util):
288 for i in range(4):
289 is_active = i % 2 == 0
290 user_util.create_user_group(users_group_active=is_active)
291 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
292 with self._patch_user_group_list():
293 groups = RepoModel().get_user_groups()
294 expected = ('id', 'icon_link', 'value_display', 'value', 'value_type')
295 for group in groups:
296 assert group['value_type'] is 'user_group'
297 for key in expected:
298 assert key in group
299
300 fake_groups = [
301 u for u in groups if u['value'].startswith('test_returns')]
302 assert len(fake_groups) == 2
303 for user in fake_groups:
304 assert user['value_display'].startswith('Group: test_returns')
305
306 def _patch_user_group_list(self):
307 def side_effect(group_list, perm_set):
308 return group_list
309 return mock.patch(
310 'rhodecode.model.repo.UserGroupList', side_effect=side_effect)
@@ -1,161 +1,219 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.db import User
24 from rhodecode.model.db import User
25 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
25 from rhodecode.tests import TEST_USER_REGULAR_LOGIN
26 from rhodecode.tests.fixture import Fixture
26 from rhodecode.tests.fixture import Fixture
27 from rhodecode.model.user_group import UserGroupModel
27 from rhodecode.model.user_group import UserGroupModel
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29
29
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33
33
34 def teardown_module(self):
34 def teardown_module(self):
35 _delete_all_user_groups()
35 _delete_all_user_groups()
36
36
37
37
38 class TestGetUserGroups(object):
39 def test_returns_filtered_list(self, backend, user_util):
40 created_groups = []
41 for i in range(4):
42 created_groups.append(
43 user_util.create_user_group(users_group_active=True))
44
45 group_filter = created_groups[-1].users_group_name[-2:]
46 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
47 with self._patch_user_group_list():
48 groups = UserGroupModel().get_user_groups(group_filter)
49
50 fake_groups = [
51 u for u in groups if u['value'].startswith('test_returns')]
52 assert len(fake_groups) == 1
53 assert fake_groups[0]['value'] == created_groups[-1].users_group_name
54 assert fake_groups[0]['value_display'].startswith(
55 'Group: test_returns')
56
57 def test_returns_limited_list(self, backend, user_util):
58 created_groups = []
59 for i in range(3):
60 created_groups.append(
61 user_util.create_user_group(users_group_active=True))
62 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
63 with self._patch_user_group_list():
64 groups = UserGroupModel().get_user_groups('test_returns')
65
66 fake_groups = [
67 u for u in groups if u['value'].startswith('test_returns')]
68 assert len(fake_groups) == 3
69
70 def test_returns_active_user_groups(self, backend, user_util):
71 for i in range(4):
72 is_active = i % 2 == 0
73 user_util.create_user_group(users_group_active=is_active)
74 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
75 with self._patch_user_group_list():
76 groups = UserGroupModel().get_user_groups()
77 expected = ('id', 'icon_link', 'value_display', 'value', 'value_type')
78 for group in groups:
79 assert group['value_type'] is 'user_group'
80 for key in expected:
81 assert key in group
82
83 fake_groups = [
84 u for u in groups if u['value'].startswith('test_returns')]
85 assert len(fake_groups) == 2
86 for user in fake_groups:
87 assert user['value_display'].startswith('Group: test_returns')
88
89 def _patch_user_group_list(self):
90 def side_effect(group_list, perm_set):
91 return group_list
92 return mock.patch(
93 'rhodecode.model.user_group.UserGroupList', side_effect=side_effect)
94
95
38 @pytest.mark.parametrize(
96 @pytest.mark.parametrize(
39 "pre_existing, regular_should_be, external_should_be, groups, "
97 "pre_existing, regular_should_be, external_should_be, groups, "
40 "expected", [
98 "expected", [
41 ([], [], [], [], []),
99 ([], [], [], [], []),
42 # no changes of regular
100 # no changes of regular
43 ([], ['regular'], [], [], ['regular']),
101 ([], ['regular'], [], [], ['regular']),
44 # not added to regular group
102 # not added to regular group
45 (['some_other'], [], [], ['some_other'], []),
103 (['some_other'], [], [], ['some_other'], []),
46 (
104 (
47 [], ['regular'], ['container'], ['container'],
105 [], ['regular'], ['container'], ['container'],
48 ['regular', 'container']
106 ['regular', 'container']
49 ),
107 ),
50 (
108 (
51 [], ['regular'], [], ['container', 'container2'],
109 [], ['regular'], [], ['container', 'container2'],
52 ['regular', 'container', 'container2']
110 ['regular', 'container', 'container2']
53 ),
111 ),
54 # remove not used
112 # remove not used
55 ([], ['regular'], ['other'], [], ['regular']),
113 ([], ['regular'], ['other'], [], ['regular']),
56 (
114 (
57 ['some_other'], ['regular'], ['other', 'container'],
115 ['some_other'], ['regular'], ['other', 'container'],
58 ['container', 'container2'],
116 ['container', 'container2'],
59 ['regular', 'container', 'container2']
117 ['regular', 'container', 'container2']
60 ),
118 ),
61 ])
119 ])
62 def test_enforce_groups(pre_existing, regular_should_be,
120 def test_enforce_groups(pre_existing, regular_should_be,
63 external_should_be, groups, expected, backend_hg):
121 external_should_be, groups, expected, backend_hg):
64 # TODO: anderson: adding backend_hg fixture so it sets up the database
122 # TODO: anderson: adding backend_hg fixture so it sets up the database
65 # for when running this file alone
123 # for when running this file alone
66 _delete_all_user_groups()
124 _delete_all_user_groups()
67
125
68 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
126 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
69 for gr in pre_existing:
127 for gr in pre_existing:
70 gr = fixture.create_user_group(gr)
128 gr = fixture.create_user_group(gr)
71 Session().commit()
129 Session().commit()
72
130
73 # make sure use is just in those groups
131 # make sure use is just in those groups
74 for gr in regular_should_be:
132 for gr in regular_should_be:
75 gr = fixture.create_user_group(gr)
133 gr = fixture.create_user_group(gr)
76 Session().commit()
134 Session().commit()
77 UserGroupModel().add_user_to_group(gr, user)
135 UserGroupModel().add_user_to_group(gr, user)
78 Session().commit()
136 Session().commit()
79
137
80 # now special external groups created by auth plugins
138 # now special external groups created by auth plugins
81 for gr in external_should_be:
139 for gr in external_should_be:
82 gr = fixture.create_user_group(
140 gr = fixture.create_user_group(
83 gr, user_group_data={'extern_type': 'container'})
141 gr, user_group_data={'extern_type': 'container'})
84 Session().commit()
142 Session().commit()
85 UserGroupModel().add_user_to_group(gr, user)
143 UserGroupModel().add_user_to_group(gr, user)
86 Session().commit()
144 Session().commit()
87
145
88 UserGroupModel().enforce_groups(user, groups, 'container')
146 UserGroupModel().enforce_groups(user, groups, 'container')
89 Session().commit()
147 Session().commit()
90
148
91 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
149 user = User.get_by_username(TEST_USER_REGULAR_LOGIN)
92 in_groups = user.group_member
150 in_groups = user.group_member
93
151
94 expected.sort()
152 expected.sort()
95 assert (
153 assert (
96 expected == sorted(x.users_group.users_group_name for x in in_groups))
154 expected == sorted(x.users_group.users_group_name for x in in_groups))
97
155
98
156
99 def _delete_all_user_groups():
157 def _delete_all_user_groups():
100 for gr in UserGroupModel.get_all():
158 for gr in UserGroupModel.get_all():
101 fixture.destroy_user_group(gr)
159 fixture.destroy_user_group(gr)
102 Session().commit()
160 Session().commit()
103
161
104
162
105 def test_add_and_remove_user_from_group(user_regular, user_util):
163 def test_add_and_remove_user_from_group(user_regular, user_util):
106 user_group = user_util.create_user_group()
164 user_group = user_util.create_user_group()
107 assert user_group.members == []
165 assert user_group.members == []
108 UserGroupModel().add_user_to_group(user_group, user_regular)
166 UserGroupModel().add_user_to_group(user_group, user_regular)
109 Session().commit()
167 Session().commit()
110 assert user_group.members[0].user == user_regular
168 assert user_group.members[0].user == user_regular
111 UserGroupModel().remove_user_from_group(user_group, user_regular)
169 UserGroupModel().remove_user_from_group(user_group, user_regular)
112 Session().commit()
170 Session().commit()
113 assert user_group.members == []
171 assert user_group.members == []
114
172
115
173
116 @pytest.mark.parametrize('data, expected', [
174 @pytest.mark.parametrize('data, expected', [
117 ([], []),
175 ([], []),
118 ([{"member_user_id": 1, "type": "new"}], [1]),
176 ([{"member_user_id": 1, "type": "new"}], [1]),
119 ([{"member_user_id": 1, "type": "new"},
177 ([{"member_user_id": 1, "type": "new"},
120 {"member_user_id": 1, "type": "existing"}], [1]),
178 {"member_user_id": 1, "type": "existing"}], [1]),
121 ([{"member_user_id": 1, "type": "new"},
179 ([{"member_user_id": 1, "type": "new"},
122 {"member_user_id": 2, "type": "new"},
180 {"member_user_id": 2, "type": "new"},
123 {"member_user_id": 3, "type": "remove"}], [1, 2])
181 {"member_user_id": 3, "type": "remove"}], [1, 2])
124 ])
182 ])
125 def test_clean_members_data(data, expected):
183 def test_clean_members_data(data, expected):
126 cleaned = UserGroupModel()._clean_members_data(data)
184 cleaned = UserGroupModel()._clean_members_data(data)
127 assert cleaned == expected
185 assert cleaned == expected
128
186
129
187
130 def _create_test_members():
188 def _create_test_members():
131 members = []
189 members = []
132 for member_number in range(3):
190 for member_number in range(3):
133 member = mock.Mock()
191 member = mock.Mock()
134 member.user_id = member_number + 1
192 member.user_id = member_number + 1
135 member.user.user_id = member_number + 1
193 member.user.user_id = member_number + 1
136 members.append(member)
194 members.append(member)
137 return members
195 return members
138
196
139
197
140 def test_get_added_and_removed_users():
198 def test_get_added_and_removed_users():
141 members = _create_test_members()
199 members = _create_test_members()
142 mock_user_group = mock.Mock()
200 mock_user_group = mock.Mock()
143 mock_user_group.members = [members[0], members[1]]
201 mock_user_group.members = [members[0], members[1]]
144 new_users_list = [members[1].user.user_id, members[2].user.user_id]
202 new_users_list = [members[1].user.user_id, members[2].user.user_id]
145 model = UserGroupModel()
203 model = UserGroupModel()
146
204
147 added, removed = model._get_added_and_removed_user_ids(
205 added, removed = model._get_added_and_removed_user_ids(
148 mock_user_group, new_users_list)
206 mock_user_group, new_users_list)
149
207
150 assert added == [members[2].user.user_id]
208 assert added == [members[2].user.user_id]
151 assert removed == [members[0].user.user_id]
209 assert removed == [members[0].user.user_id]
152
210
153
211
154 def test_set_users_as_members_and_find_user_in_group(
212 def test_set_users_as_members_and_find_user_in_group(
155 user_util, user_regular, user_admin):
213 user_util, user_regular, user_admin):
156 user_group = user_util.create_user_group()
214 user_group = user_util.create_user_group()
157 assert len(user_group.members) == 0
215 assert len(user_group.members) == 0
158 user_list = [user_regular.user_id, user_admin.user_id]
216 user_list = [user_regular.user_id, user_admin.user_id]
159 UserGroupModel()._set_users_as_members(user_group, user_list)
217 UserGroupModel()._set_users_as_members(user_group, user_list)
160 assert len(user_group.members) == 2
218 assert len(user_group.members) == 2
161 assert UserGroupModel()._find_user_in_group(user_regular, user_group)
219 assert UserGroupModel()._find_user_in_group(user_regular, user_group)
General Comments 0
You need to be logged in to leave comments. Login now