##// END OF EJS Templates
security: escape always the provided user data like firstname/lastname.
marcink -
r1780:73cbe636 default
parent child Browse files
Show More
@@ -1,902 +1,902 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 users model for RhodeCode
22 users model for RhodeCode
23 """
23 """
24
24
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 import datetime
28 import datetime
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 import ipaddress
31 import ipaddress
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.sql.expression import true, false
34
33
35 from rhodecode import events
34 from rhodecode import events
36 from rhodecode.lib.user_log_filter import user_log_filter
35 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.utils2 import (
36 from rhodecode.lib.utils2 import (
38 safe_unicode, get_current_rhodecode_user, action_logger_generic,
37 safe_unicode, get_current_rhodecode_user, action_logger_generic,
39 AttributeDict, str2bool)
38 AttributeDict, str2bool)
39 from rhodecode.lib.exceptions import (
40 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
40 from rhodecode.lib.caching_query import FromCache
42 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.model import BaseModel
43 from rhodecode.model import BaseModel
42 from rhodecode.model.auth_token import AuthTokenModel
44 from rhodecode.model.auth_token import AuthTokenModel
43 from rhodecode.model.db import (_hash_key,
45 from rhodecode.model.db import (
44 or_, joinedload, User, UserToPerm, UserEmailMap, UserIpMap, UserLog)
46 _hash_key, true, false, or_, joinedload, User, UserToPerm,
45 from rhodecode.lib.exceptions import (
47 UserEmailMap, UserIpMap, UserLog)
46 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
47 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UserModel(BaseModel):
55 class UserModel(BaseModel):
56 cls = User
56 cls = User
57
57
58 def get(self, user_id, cache=False):
58 def get(self, user_id, cache=False):
59 user = self.sa.query(User)
59 user = self.sa.query(User)
60 if cache:
60 if cache:
61 user = user.options(
61 user = user.options(
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
62 FromCache("sql_cache_short", "get_user_%s" % user_id))
63 return user.get(user_id)
63 return user.get(user_id)
64
64
65 def get_user(self, user):
65 def get_user(self, user):
66 return self._get_user(user)
66 return self._get_user(user)
67
67
68 def _serialize_user(self, user):
68 def _serialize_user(self, user):
69 import rhodecode.lib.helpers as h
69 import rhodecode.lib.helpers as h
70
70
71 return {
71 return {
72 'id': user.user_id,
72 'id': user.user_id,
73 'first_name': user.name,
73 'first_name': h.escape(user.name),
74 'last_name': user.lastname,
74 'last_name': h.escape(user.lastname),
75 'username': user.username,
75 'username': user.username,
76 'email': user.email,
76 'email': user.email,
77 'icon_link': h.gravatar_url(user.email, 30),
77 'icon_link': h.gravatar_url(user.email, 30),
78 'value_display': h.person(user),
78 'value_display': h.escape(h.person(user)),
79 'value': user.username,
79 'value': user.username,
80 'value_type': 'user',
80 'value_type': 'user',
81 'active': user.active,
81 'active': user.active,
82 }
82 }
83
83
84 def get_users(self, name_contains=None, limit=20, only_active=True):
84 def get_users(self, name_contains=None, limit=20, only_active=True):
85
85
86 query = self.sa.query(User)
86 query = self.sa.query(User)
87 if only_active:
87 if only_active:
88 query = query.filter(User.active == true())
88 query = query.filter(User.active == true())
89
89
90 if name_contains:
90 if name_contains:
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
91 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
92 query = query.filter(
92 query = query.filter(
93 or_(
93 or_(
94 User.name.ilike(ilike_expression),
94 User.name.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
95 User.lastname.ilike(ilike_expression),
96 User.username.ilike(ilike_expression)
96 User.username.ilike(ilike_expression)
97 )
97 )
98 )
98 )
99 query = query.limit(limit)
99 query = query.limit(limit)
100 users = query.all()
100 users = query.all()
101
101
102 _users = [
102 _users = [
103 self._serialize_user(user) for user in users
103 self._serialize_user(user) for user in users
104 ]
104 ]
105 return _users
105 return _users
106
106
107 def get_by_username(self, username, cache=False, case_insensitive=False):
107 def get_by_username(self, username, cache=False, case_insensitive=False):
108
108
109 if case_insensitive:
109 if case_insensitive:
110 user = self.sa.query(User).filter(User.username.ilike(username))
110 user = self.sa.query(User).filter(User.username.ilike(username))
111 else:
111 else:
112 user = self.sa.query(User)\
112 user = self.sa.query(User)\
113 .filter(User.username == username)
113 .filter(User.username == username)
114 if cache:
114 if cache:
115 name_key = _hash_key(username)
115 name_key = _hash_key(username)
116 user = user.options(
116 user = user.options(
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
117 FromCache("sql_cache_short", "get_user_%s" % name_key))
118 return user.scalar()
118 return user.scalar()
119
119
120 def get_by_email(self, email, cache=False, case_insensitive=False):
120 def get_by_email(self, email, cache=False, case_insensitive=False):
121 return User.get_by_email(email, case_insensitive, cache)
121 return User.get_by_email(email, case_insensitive, cache)
122
122
123 def get_by_auth_token(self, auth_token, cache=False):
123 def get_by_auth_token(self, auth_token, cache=False):
124 return User.get_by_auth_token(auth_token, cache)
124 return User.get_by_auth_token(auth_token, cache)
125
125
126 def get_active_user_count(self, cache=False):
126 def get_active_user_count(self, cache=False):
127 return User.query().filter(
127 return User.query().filter(
128 User.active == True).filter(
128 User.active == True).filter(
129 User.username != User.DEFAULT_USER).count()
129 User.username != User.DEFAULT_USER).count()
130
130
131 def create(self, form_data, cur_user=None):
131 def create(self, form_data, cur_user=None):
132 if not cur_user:
132 if not cur_user:
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
133 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
134
134
135 user_data = {
135 user_data = {
136 'username': form_data['username'],
136 'username': form_data['username'],
137 'password': form_data['password'],
137 'password': form_data['password'],
138 'email': form_data['email'],
138 'email': form_data['email'],
139 'firstname': form_data['firstname'],
139 'firstname': form_data['firstname'],
140 'lastname': form_data['lastname'],
140 'lastname': form_data['lastname'],
141 'active': form_data['active'],
141 'active': form_data['active'],
142 'extern_type': form_data['extern_type'],
142 'extern_type': form_data['extern_type'],
143 'extern_name': form_data['extern_name'],
143 'extern_name': form_data['extern_name'],
144 'admin': False,
144 'admin': False,
145 'cur_user': cur_user
145 'cur_user': cur_user
146 }
146 }
147
147
148 if 'create_repo_group' in form_data:
148 if 'create_repo_group' in form_data:
149 user_data['create_repo_group'] = str2bool(
149 user_data['create_repo_group'] = str2bool(
150 form_data.get('create_repo_group'))
150 form_data.get('create_repo_group'))
151
151
152 try:
152 try:
153 if form_data.get('password_change'):
153 if form_data.get('password_change'):
154 user_data['force_password_change'] = True
154 user_data['force_password_change'] = True
155 return UserModel().create_or_update(**user_data)
155 return UserModel().create_or_update(**user_data)
156 except Exception:
156 except Exception:
157 log.error(traceback.format_exc())
157 log.error(traceback.format_exc())
158 raise
158 raise
159
159
160 def update_user(self, user, skip_attrs=None, **kwargs):
160 def update_user(self, user, skip_attrs=None, **kwargs):
161 from rhodecode.lib.auth import get_crypt_password
161 from rhodecode.lib.auth import get_crypt_password
162
162
163 user = self._get_user(user)
163 user = self._get_user(user)
164 if user.username == User.DEFAULT_USER:
164 if user.username == User.DEFAULT_USER:
165 raise DefaultUserException(
165 raise DefaultUserException(
166 _("You can't Edit this user since it's"
166 _("You can't Edit this user since it's"
167 " crucial for entire application"))
167 " crucial for entire application"))
168
168
169 # first store only defaults
169 # first store only defaults
170 user_attrs = {
170 user_attrs = {
171 'updating_user_id': user.user_id,
171 'updating_user_id': user.user_id,
172 'username': user.username,
172 'username': user.username,
173 'password': user.password,
173 'password': user.password,
174 'email': user.email,
174 'email': user.email,
175 'firstname': user.name,
175 'firstname': user.name,
176 'lastname': user.lastname,
176 'lastname': user.lastname,
177 'active': user.active,
177 'active': user.active,
178 'admin': user.admin,
178 'admin': user.admin,
179 'extern_name': user.extern_name,
179 'extern_name': user.extern_name,
180 'extern_type': user.extern_type,
180 'extern_type': user.extern_type,
181 'language': user.user_data.get('language')
181 'language': user.user_data.get('language')
182 }
182 }
183
183
184 # in case there's new_password, that comes from form, use it to
184 # in case there's new_password, that comes from form, use it to
185 # store password
185 # store password
186 if kwargs.get('new_password'):
186 if kwargs.get('new_password'):
187 kwargs['password'] = kwargs['new_password']
187 kwargs['password'] = kwargs['new_password']
188
188
189 # cleanups, my_account password change form
189 # cleanups, my_account password change form
190 kwargs.pop('current_password', None)
190 kwargs.pop('current_password', None)
191 kwargs.pop('new_password', None)
191 kwargs.pop('new_password', None)
192
192
193 # cleanups, user edit password change form
193 # cleanups, user edit password change form
194 kwargs.pop('password_confirmation', None)
194 kwargs.pop('password_confirmation', None)
195 kwargs.pop('password_change', None)
195 kwargs.pop('password_change', None)
196
196
197 # create repo group on user creation
197 # create repo group on user creation
198 kwargs.pop('create_repo_group', None)
198 kwargs.pop('create_repo_group', None)
199
199
200 # legacy forms send name, which is the firstname
200 # legacy forms send name, which is the firstname
201 firstname = kwargs.pop('name', None)
201 firstname = kwargs.pop('name', None)
202 if firstname:
202 if firstname:
203 kwargs['firstname'] = firstname
203 kwargs['firstname'] = firstname
204
204
205 for k, v in kwargs.items():
205 for k, v in kwargs.items():
206 # skip if we don't want to update this
206 # skip if we don't want to update this
207 if skip_attrs and k in skip_attrs:
207 if skip_attrs and k in skip_attrs:
208 continue
208 continue
209
209
210 user_attrs[k] = v
210 user_attrs[k] = v
211
211
212 try:
212 try:
213 return self.create_or_update(**user_attrs)
213 return self.create_or_update(**user_attrs)
214 except Exception:
214 except Exception:
215 log.error(traceback.format_exc())
215 log.error(traceback.format_exc())
216 raise
216 raise
217
217
218 def create_or_update(
218 def create_or_update(
219 self, username, password, email, firstname='', lastname='',
219 self, username, password, email, firstname='', lastname='',
220 active=True, admin=False, extern_type=None, extern_name=None,
220 active=True, admin=False, extern_type=None, extern_name=None,
221 cur_user=None, plugin=None, force_password_change=False,
221 cur_user=None, plugin=None, force_password_change=False,
222 allow_to_create_user=True, create_repo_group=None,
222 allow_to_create_user=True, create_repo_group=None,
223 updating_user_id=None, language=None, strict_creation_check=True):
223 updating_user_id=None, language=None, strict_creation_check=True):
224 """
224 """
225 Creates a new instance if not found, or updates current one
225 Creates a new instance if not found, or updates current one
226
226
227 :param username:
227 :param username:
228 :param password:
228 :param password:
229 :param email:
229 :param email:
230 :param firstname:
230 :param firstname:
231 :param lastname:
231 :param lastname:
232 :param active:
232 :param active:
233 :param admin:
233 :param admin:
234 :param extern_type:
234 :param extern_type:
235 :param extern_name:
235 :param extern_name:
236 :param cur_user:
236 :param cur_user:
237 :param plugin: optional plugin this method was called from
237 :param plugin: optional plugin this method was called from
238 :param force_password_change: toggles new or existing user flag
238 :param force_password_change: toggles new or existing user flag
239 for password change
239 for password change
240 :param allow_to_create_user: Defines if the method can actually create
240 :param allow_to_create_user: Defines if the method can actually create
241 new users
241 new users
242 :param create_repo_group: Defines if the method should also
242 :param create_repo_group: Defines if the method should also
243 create an repo group with user name, and owner
243 create an repo group with user name, and owner
244 :param updating_user_id: if we set it up this is the user we want to
244 :param updating_user_id: if we set it up this is the user we want to
245 update this allows to editing username.
245 update this allows to editing username.
246 :param language: language of user from interface.
246 :param language: language of user from interface.
247
247
248 :returns: new User object with injected `is_new_user` attribute.
248 :returns: new User object with injected `is_new_user` attribute.
249 """
249 """
250 if not cur_user:
250 if not cur_user:
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
251 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
252
252
253 from rhodecode.lib.auth import (
253 from rhodecode.lib.auth import (
254 get_crypt_password, check_password, generate_auth_token)
254 get_crypt_password, check_password, generate_auth_token)
255 from rhodecode.lib.hooks_base import (
255 from rhodecode.lib.hooks_base import (
256 log_create_user, check_allowed_create_user)
256 log_create_user, check_allowed_create_user)
257
257
258 def _password_change(new_user, password):
258 def _password_change(new_user, password):
259 # empty password
259 # empty password
260 if not new_user.password:
260 if not new_user.password:
261 return False
261 return False
262
262
263 # password check is only needed for RhodeCode internal auth calls
263 # password check is only needed for RhodeCode internal auth calls
264 # in case it's a plugin we don't care
264 # in case it's a plugin we don't care
265 if not plugin:
265 if not plugin:
266
266
267 # first check if we gave crypted password back, and if it
267 # first check if we gave crypted password back, and if it
268 # matches it's not password change
268 # matches it's not password change
269 if new_user.password == password:
269 if new_user.password == password:
270 return False
270 return False
271
271
272 password_match = check_password(password, new_user.password)
272 password_match = check_password(password, new_user.password)
273 if not password_match:
273 if not password_match:
274 return True
274 return True
275
275
276 return False
276 return False
277
277
278 # read settings on default personal repo group creation
278 # read settings on default personal repo group creation
279 if create_repo_group is None:
279 if create_repo_group is None:
280 default_create_repo_group = RepoGroupModel()\
280 default_create_repo_group = RepoGroupModel()\
281 .get_default_create_personal_repo_group()
281 .get_default_create_personal_repo_group()
282 create_repo_group = default_create_repo_group
282 create_repo_group = default_create_repo_group
283
283
284 user_data = {
284 user_data = {
285 'username': username,
285 'username': username,
286 'password': password,
286 'password': password,
287 'email': email,
287 'email': email,
288 'firstname': firstname,
288 'firstname': firstname,
289 'lastname': lastname,
289 'lastname': lastname,
290 'active': active,
290 'active': active,
291 'admin': admin
291 'admin': admin
292 }
292 }
293
293
294 if updating_user_id:
294 if updating_user_id:
295 log.debug('Checking for existing account in RhodeCode '
295 log.debug('Checking for existing account in RhodeCode '
296 'database with user_id `%s` ' % (updating_user_id,))
296 'database with user_id `%s` ' % (updating_user_id,))
297 user = User.get(updating_user_id)
297 user = User.get(updating_user_id)
298 else:
298 else:
299 log.debug('Checking for existing account in RhodeCode '
299 log.debug('Checking for existing account in RhodeCode '
300 'database with username `%s` ' % (username,))
300 'database with username `%s` ' % (username,))
301 user = User.get_by_username(username, case_insensitive=True)
301 user = User.get_by_username(username, case_insensitive=True)
302
302
303 if user is None:
303 if user is None:
304 # we check internal flag if this method is actually allowed to
304 # we check internal flag if this method is actually allowed to
305 # create new user
305 # create new user
306 if not allow_to_create_user:
306 if not allow_to_create_user:
307 msg = ('Method wants to create new user, but it is not '
307 msg = ('Method wants to create new user, but it is not '
308 'allowed to do so')
308 'allowed to do so')
309 log.warning(msg)
309 log.warning(msg)
310 raise NotAllowedToCreateUserError(msg)
310 raise NotAllowedToCreateUserError(msg)
311
311
312 log.debug('Creating new user %s', username)
312 log.debug('Creating new user %s', username)
313
313
314 # only if we create user that is active
314 # only if we create user that is active
315 new_active_user = active
315 new_active_user = active
316 if new_active_user and strict_creation_check:
316 if new_active_user and strict_creation_check:
317 # raises UserCreationError if it's not allowed for any reason to
317 # raises UserCreationError if it's not allowed for any reason to
318 # create new active user, this also executes pre-create hooks
318 # create new active user, this also executes pre-create hooks
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
319 check_allowed_create_user(user_data, cur_user, strict_check=True)
320 events.trigger(events.UserPreCreate(user_data))
320 events.trigger(events.UserPreCreate(user_data))
321 new_user = User()
321 new_user = User()
322 edit = False
322 edit = False
323 else:
323 else:
324 log.debug('updating user %s', username)
324 log.debug('updating user %s', username)
325 events.trigger(events.UserPreUpdate(user, user_data))
325 events.trigger(events.UserPreUpdate(user, user_data))
326 new_user = user
326 new_user = user
327 edit = True
327 edit = True
328
328
329 # we're not allowed to edit default user
329 # we're not allowed to edit default user
330 if user.username == User.DEFAULT_USER:
330 if user.username == User.DEFAULT_USER:
331 raise DefaultUserException(
331 raise DefaultUserException(
332 _("You can't edit this user (`%(username)s`) since it's "
332 _("You can't edit this user (`%(username)s`) since it's "
333 "crucial for entire application") % {'username': user.username})
333 "crucial for entire application") % {'username': user.username})
334
334
335 # inject special attribute that will tell us if User is new or old
335 # inject special attribute that will tell us if User is new or old
336 new_user.is_new_user = not edit
336 new_user.is_new_user = not edit
337 # for users that didn's specify auth type, we use RhodeCode built in
337 # for users that didn's specify auth type, we use RhodeCode built in
338 from rhodecode.authentication.plugins import auth_rhodecode
338 from rhodecode.authentication.plugins import auth_rhodecode
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
339 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
340 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
341
341
342 try:
342 try:
343 new_user.username = username
343 new_user.username = username
344 new_user.admin = admin
344 new_user.admin = admin
345 new_user.email = email
345 new_user.email = email
346 new_user.active = active
346 new_user.active = active
347 new_user.extern_name = safe_unicode(extern_name)
347 new_user.extern_name = safe_unicode(extern_name)
348 new_user.extern_type = safe_unicode(extern_type)
348 new_user.extern_type = safe_unicode(extern_type)
349 new_user.name = firstname
349 new_user.name = firstname
350 new_user.lastname = lastname
350 new_user.lastname = lastname
351
351
352 # set password only if creating an user or password is changed
352 # set password only if creating an user or password is changed
353 if not edit or _password_change(new_user, password):
353 if not edit or _password_change(new_user, password):
354 reason = 'new password' if edit else 'new user'
354 reason = 'new password' if edit else 'new user'
355 log.debug('Updating password reason=>%s', reason)
355 log.debug('Updating password reason=>%s', reason)
356 new_user.password = get_crypt_password(password) if password else None
356 new_user.password = get_crypt_password(password) if password else None
357
357
358 if force_password_change:
358 if force_password_change:
359 new_user.update_userdata(force_password_change=True)
359 new_user.update_userdata(force_password_change=True)
360 if language:
360 if language:
361 new_user.update_userdata(language=language)
361 new_user.update_userdata(language=language)
362 new_user.update_userdata(notification_status=True)
362 new_user.update_userdata(notification_status=True)
363
363
364 self.sa.add(new_user)
364 self.sa.add(new_user)
365
365
366 if not edit and create_repo_group:
366 if not edit and create_repo_group:
367 RepoGroupModel().create_personal_repo_group(
367 RepoGroupModel().create_personal_repo_group(
368 new_user, commit_early=False)
368 new_user, commit_early=False)
369
369
370 if not edit:
370 if not edit:
371 # add the RSS token
371 # add the RSS token
372 AuthTokenModel().create(username,
372 AuthTokenModel().create(username,
373 description='Generated feed token',
373 description='Generated feed token',
374 role=AuthTokenModel.cls.ROLE_FEED)
374 role=AuthTokenModel.cls.ROLE_FEED)
375 log_create_user(created_by=cur_user, **new_user.get_dict())
375 log_create_user(created_by=cur_user, **new_user.get_dict())
376 events.trigger(events.UserPostCreate(user_data))
376 events.trigger(events.UserPostCreate(user_data))
377 return new_user
377 return new_user
378 except (DatabaseError,):
378 except (DatabaseError,):
379 log.error(traceback.format_exc())
379 log.error(traceback.format_exc())
380 raise
380 raise
381
381
382 def create_registration(self, form_data):
382 def create_registration(self, form_data):
383 from rhodecode.model.notification import NotificationModel
383 from rhodecode.model.notification import NotificationModel
384 from rhodecode.model.notification import EmailNotificationModel
384 from rhodecode.model.notification import EmailNotificationModel
385
385
386 try:
386 try:
387 form_data['admin'] = False
387 form_data['admin'] = False
388 form_data['extern_name'] = 'rhodecode'
388 form_data['extern_name'] = 'rhodecode'
389 form_data['extern_type'] = 'rhodecode'
389 form_data['extern_type'] = 'rhodecode'
390 new_user = self.create(form_data)
390 new_user = self.create(form_data)
391
391
392 self.sa.add(new_user)
392 self.sa.add(new_user)
393 self.sa.flush()
393 self.sa.flush()
394
394
395 user_data = new_user.get_dict()
395 user_data = new_user.get_dict()
396 kwargs = {
396 kwargs = {
397 # use SQLALCHEMY safe dump of user data
397 # use SQLALCHEMY safe dump of user data
398 'user': AttributeDict(user_data),
398 'user': AttributeDict(user_data),
399 'date': datetime.datetime.now()
399 'date': datetime.datetime.now()
400 }
400 }
401 notification_type = EmailNotificationModel.TYPE_REGISTRATION
401 notification_type = EmailNotificationModel.TYPE_REGISTRATION
402 # pre-generate the subject for notification itself
402 # pre-generate the subject for notification itself
403 (subject,
403 (subject,
404 _h, _e, # we don't care about those
404 _h, _e, # we don't care about those
405 body_plaintext) = EmailNotificationModel().render_email(
405 body_plaintext) = EmailNotificationModel().render_email(
406 notification_type, **kwargs)
406 notification_type, **kwargs)
407
407
408 # create notification objects, and emails
408 # create notification objects, and emails
409 NotificationModel().create(
409 NotificationModel().create(
410 created_by=new_user,
410 created_by=new_user,
411 notification_subject=subject,
411 notification_subject=subject,
412 notification_body=body_plaintext,
412 notification_body=body_plaintext,
413 notification_type=notification_type,
413 notification_type=notification_type,
414 recipients=None, # all admins
414 recipients=None, # all admins
415 email_kwargs=kwargs,
415 email_kwargs=kwargs,
416 )
416 )
417
417
418 return new_user
418 return new_user
419 except Exception:
419 except Exception:
420 log.error(traceback.format_exc())
420 log.error(traceback.format_exc())
421 raise
421 raise
422
422
423 def _handle_user_repos(self, username, repositories, handle_mode=None):
423 def _handle_user_repos(self, username, repositories, handle_mode=None):
424 _superadmin = self.cls.get_first_super_admin()
424 _superadmin = self.cls.get_first_super_admin()
425 left_overs = True
425 left_overs = True
426
426
427 from rhodecode.model.repo import RepoModel
427 from rhodecode.model.repo import RepoModel
428
428
429 if handle_mode == 'detach':
429 if handle_mode == 'detach':
430 for obj in repositories:
430 for obj in repositories:
431 obj.user = _superadmin
431 obj.user = _superadmin
432 # set description we know why we super admin now owns
432 # set description we know why we super admin now owns
433 # additional repositories that were orphaned !
433 # additional repositories that were orphaned !
434 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
434 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
435 self.sa.add(obj)
435 self.sa.add(obj)
436 left_overs = False
436 left_overs = False
437 elif handle_mode == 'delete':
437 elif handle_mode == 'delete':
438 for obj in repositories:
438 for obj in repositories:
439 RepoModel().delete(obj, forks='detach')
439 RepoModel().delete(obj, forks='detach')
440 left_overs = False
440 left_overs = False
441
441
442 # if nothing is done we have left overs left
442 # if nothing is done we have left overs left
443 return left_overs
443 return left_overs
444
444
445 def _handle_user_repo_groups(self, username, repository_groups,
445 def _handle_user_repo_groups(self, username, repository_groups,
446 handle_mode=None):
446 handle_mode=None):
447 _superadmin = self.cls.get_first_super_admin()
447 _superadmin = self.cls.get_first_super_admin()
448 left_overs = True
448 left_overs = True
449
449
450 from rhodecode.model.repo_group import RepoGroupModel
450 from rhodecode.model.repo_group import RepoGroupModel
451
451
452 if handle_mode == 'detach':
452 if handle_mode == 'detach':
453 for r in repository_groups:
453 for r in repository_groups:
454 r.user = _superadmin
454 r.user = _superadmin
455 # set description we know why we super admin now owns
455 # set description we know why we super admin now owns
456 # additional repositories that were orphaned !
456 # additional repositories that were orphaned !
457 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
457 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
458 self.sa.add(r)
458 self.sa.add(r)
459 left_overs = False
459 left_overs = False
460 elif handle_mode == 'delete':
460 elif handle_mode == 'delete':
461 for r in repository_groups:
461 for r in repository_groups:
462 RepoGroupModel().delete(r)
462 RepoGroupModel().delete(r)
463 left_overs = False
463 left_overs = False
464
464
465 # if nothing is done we have left overs left
465 # if nothing is done we have left overs left
466 return left_overs
466 return left_overs
467
467
468 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
468 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
469 _superadmin = self.cls.get_first_super_admin()
469 _superadmin = self.cls.get_first_super_admin()
470 left_overs = True
470 left_overs = True
471
471
472 from rhodecode.model.user_group import UserGroupModel
472 from rhodecode.model.user_group import UserGroupModel
473
473
474 if handle_mode == 'detach':
474 if handle_mode == 'detach':
475 for r in user_groups:
475 for r in user_groups:
476 for user_user_group_to_perm in r.user_user_group_to_perm:
476 for user_user_group_to_perm in r.user_user_group_to_perm:
477 if user_user_group_to_perm.user.username == username:
477 if user_user_group_to_perm.user.username == username:
478 user_user_group_to_perm.user = _superadmin
478 user_user_group_to_perm.user = _superadmin
479 r.user = _superadmin
479 r.user = _superadmin
480 # set description we know why we super admin now owns
480 # set description we know why we super admin now owns
481 # additional repositories that were orphaned !
481 # additional repositories that were orphaned !
482 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
482 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
483 self.sa.add(r)
483 self.sa.add(r)
484 left_overs = False
484 left_overs = False
485 elif handle_mode == 'delete':
485 elif handle_mode == 'delete':
486 for r in user_groups:
486 for r in user_groups:
487 UserGroupModel().delete(r)
487 UserGroupModel().delete(r)
488 left_overs = False
488 left_overs = False
489
489
490 # if nothing is done we have left overs left
490 # if nothing is done we have left overs left
491 return left_overs
491 return left_overs
492
492
493 def delete(self, user, cur_user=None, handle_repos=None,
493 def delete(self, user, cur_user=None, handle_repos=None,
494 handle_repo_groups=None, handle_user_groups=None):
494 handle_repo_groups=None, handle_user_groups=None):
495 if not cur_user:
495 if not cur_user:
496 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
496 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
497 user = self._get_user(user)
497 user = self._get_user(user)
498
498
499 try:
499 try:
500 if user.username == User.DEFAULT_USER:
500 if user.username == User.DEFAULT_USER:
501 raise DefaultUserException(
501 raise DefaultUserException(
502 _(u"You can't remove this user since it's"
502 _(u"You can't remove this user since it's"
503 u" crucial for entire application"))
503 u" crucial for entire application"))
504
504
505 left_overs = self._handle_user_repos(
505 left_overs = self._handle_user_repos(
506 user.username, user.repositories, handle_repos)
506 user.username, user.repositories, handle_repos)
507 if left_overs and user.repositories:
507 if left_overs and user.repositories:
508 repos = [x.repo_name for x in user.repositories]
508 repos = [x.repo_name for x in user.repositories]
509 raise UserOwnsReposException(
509 raise UserOwnsReposException(
510 _(u'user "%s" still owns %s repositories and cannot be '
510 _(u'user "%s" still owns %s repositories and cannot be '
511 u'removed. Switch owners or remove those repositories:%s')
511 u'removed. Switch owners or remove those repositories:%s')
512 % (user.username, len(repos), ', '.join(repos)))
512 % (user.username, len(repos), ', '.join(repos)))
513
513
514 left_overs = self._handle_user_repo_groups(
514 left_overs = self._handle_user_repo_groups(
515 user.username, user.repository_groups, handle_repo_groups)
515 user.username, user.repository_groups, handle_repo_groups)
516 if left_overs and user.repository_groups:
516 if left_overs and user.repository_groups:
517 repo_groups = [x.group_name for x in user.repository_groups]
517 repo_groups = [x.group_name for x in user.repository_groups]
518 raise UserOwnsRepoGroupsException(
518 raise UserOwnsRepoGroupsException(
519 _(u'user "%s" still owns %s repository groups and cannot be '
519 _(u'user "%s" still owns %s repository groups and cannot be '
520 u'removed. Switch owners or remove those repository groups:%s')
520 u'removed. Switch owners or remove those repository groups:%s')
521 % (user.username, len(repo_groups), ', '.join(repo_groups)))
521 % (user.username, len(repo_groups), ', '.join(repo_groups)))
522
522
523 left_overs = self._handle_user_user_groups(
523 left_overs = self._handle_user_user_groups(
524 user.username, user.user_groups, handle_user_groups)
524 user.username, user.user_groups, handle_user_groups)
525 if left_overs and user.user_groups:
525 if left_overs and user.user_groups:
526 user_groups = [x.users_group_name for x in user.user_groups]
526 user_groups = [x.users_group_name for x in user.user_groups]
527 raise UserOwnsUserGroupsException(
527 raise UserOwnsUserGroupsException(
528 _(u'user "%s" still owns %s user groups and cannot be '
528 _(u'user "%s" still owns %s user groups and cannot be '
529 u'removed. Switch owners or remove those user groups:%s')
529 u'removed. Switch owners or remove those user groups:%s')
530 % (user.username, len(user_groups), ', '.join(user_groups)))
530 % (user.username, len(user_groups), ', '.join(user_groups)))
531
531
532 # we might change the user data with detach/delete, make sure
532 # we might change the user data with detach/delete, make sure
533 # the object is marked as expired before actually deleting !
533 # the object is marked as expired before actually deleting !
534 self.sa.expire(user)
534 self.sa.expire(user)
535 self.sa.delete(user)
535 self.sa.delete(user)
536 from rhodecode.lib.hooks_base import log_delete_user
536 from rhodecode.lib.hooks_base import log_delete_user
537 log_delete_user(deleted_by=cur_user, **user.get_dict())
537 log_delete_user(deleted_by=cur_user, **user.get_dict())
538 except Exception:
538 except Exception:
539 log.error(traceback.format_exc())
539 log.error(traceback.format_exc())
540 raise
540 raise
541
541
542 def reset_password_link(self, data, pwd_reset_url):
542 def reset_password_link(self, data, pwd_reset_url):
543 from rhodecode.lib.celerylib import tasks, run_task
543 from rhodecode.lib.celerylib import tasks, run_task
544 from rhodecode.model.notification import EmailNotificationModel
544 from rhodecode.model.notification import EmailNotificationModel
545 user_email = data['email']
545 user_email = data['email']
546 try:
546 try:
547 user = User.get_by_email(user_email)
547 user = User.get_by_email(user_email)
548 if user:
548 if user:
549 log.debug('password reset user found %s', user)
549 log.debug('password reset user found %s', user)
550
550
551 email_kwargs = {
551 email_kwargs = {
552 'password_reset_url': pwd_reset_url,
552 'password_reset_url': pwd_reset_url,
553 'user': user,
553 'user': user,
554 'email': user_email,
554 'email': user_email,
555 'date': datetime.datetime.now()
555 'date': datetime.datetime.now()
556 }
556 }
557
557
558 (subject, headers, email_body,
558 (subject, headers, email_body,
559 email_body_plaintext) = EmailNotificationModel().render_email(
559 email_body_plaintext) = EmailNotificationModel().render_email(
560 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
560 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
561
561
562 recipients = [user_email]
562 recipients = [user_email]
563
563
564 action_logger_generic(
564 action_logger_generic(
565 'sending password reset email to user: {}'.format(
565 'sending password reset email to user: {}'.format(
566 user), namespace='security.password_reset')
566 user), namespace='security.password_reset')
567
567
568 run_task(tasks.send_email, recipients, subject,
568 run_task(tasks.send_email, recipients, subject,
569 email_body_plaintext, email_body)
569 email_body_plaintext, email_body)
570
570
571 else:
571 else:
572 log.debug("password reset email %s not found", user_email)
572 log.debug("password reset email %s not found", user_email)
573 except Exception:
573 except Exception:
574 log.error(traceback.format_exc())
574 log.error(traceback.format_exc())
575 return False
575 return False
576
576
577 return True
577 return True
578
578
579 def reset_password(self, data):
579 def reset_password(self, data):
580 from rhodecode.lib.celerylib import tasks, run_task
580 from rhodecode.lib.celerylib import tasks, run_task
581 from rhodecode.model.notification import EmailNotificationModel
581 from rhodecode.model.notification import EmailNotificationModel
582 from rhodecode.lib import auth
582 from rhodecode.lib import auth
583 user_email = data['email']
583 user_email = data['email']
584 pre_db = True
584 pre_db = True
585 try:
585 try:
586 user = User.get_by_email(user_email)
586 user = User.get_by_email(user_email)
587 new_passwd = auth.PasswordGenerator().gen_password(
587 new_passwd = auth.PasswordGenerator().gen_password(
588 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
588 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
589 if user:
589 if user:
590 user.password = auth.get_crypt_password(new_passwd)
590 user.password = auth.get_crypt_password(new_passwd)
591 # also force this user to reset his password !
591 # also force this user to reset his password !
592 user.update_userdata(force_password_change=True)
592 user.update_userdata(force_password_change=True)
593
593
594 Session().add(user)
594 Session().add(user)
595
595
596 # now delete the token in question
596 # now delete the token in question
597 UserApiKeys = AuthTokenModel.cls
597 UserApiKeys = AuthTokenModel.cls
598 UserApiKeys().query().filter(
598 UserApiKeys().query().filter(
599 UserApiKeys.api_key == data['token']).delete()
599 UserApiKeys.api_key == data['token']).delete()
600
600
601 Session().commit()
601 Session().commit()
602 log.info('successfully reset password for `%s`', user_email)
602 log.info('successfully reset password for `%s`', user_email)
603
603
604 if new_passwd is None:
604 if new_passwd is None:
605 raise Exception('unable to generate new password')
605 raise Exception('unable to generate new password')
606
606
607 pre_db = False
607 pre_db = False
608
608
609 email_kwargs = {
609 email_kwargs = {
610 'new_password': new_passwd,
610 'new_password': new_passwd,
611 'user': user,
611 'user': user,
612 'email': user_email,
612 'email': user_email,
613 'date': datetime.datetime.now()
613 'date': datetime.datetime.now()
614 }
614 }
615
615
616 (subject, headers, email_body,
616 (subject, headers, email_body,
617 email_body_plaintext) = EmailNotificationModel().render_email(
617 email_body_plaintext) = EmailNotificationModel().render_email(
618 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
618 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
619 **email_kwargs)
619 **email_kwargs)
620
620
621 recipients = [user_email]
621 recipients = [user_email]
622
622
623 action_logger_generic(
623 action_logger_generic(
624 'sent new password to user: {} with email: {}'.format(
624 'sent new password to user: {} with email: {}'.format(
625 user, user_email), namespace='security.password_reset')
625 user, user_email), namespace='security.password_reset')
626
626
627 run_task(tasks.send_email, recipients, subject,
627 run_task(tasks.send_email, recipients, subject,
628 email_body_plaintext, email_body)
628 email_body_plaintext, email_body)
629
629
630 except Exception:
630 except Exception:
631 log.error('Failed to update user password')
631 log.error('Failed to update user password')
632 log.error(traceback.format_exc())
632 log.error(traceback.format_exc())
633 if pre_db:
633 if pre_db:
634 # we rollback only if local db stuff fails. If it goes into
634 # we rollback only if local db stuff fails. If it goes into
635 # run_task, we're pass rollback state this wouldn't work then
635 # run_task, we're pass rollback state this wouldn't work then
636 Session().rollback()
636 Session().rollback()
637
637
638 return True
638 return True
639
639
640 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
640 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
641 """
641 """
642 Fetches auth_user by user_id,or api_key if present.
642 Fetches auth_user by user_id,or api_key if present.
643 Fills auth_user attributes with those taken from database.
643 Fills auth_user attributes with those taken from database.
644 Additionally set's is_authenitated if lookup fails
644 Additionally set's is_authenitated if lookup fails
645 present in database
645 present in database
646
646
647 :param auth_user: instance of user to set attributes
647 :param auth_user: instance of user to set attributes
648 :param user_id: user id to fetch by
648 :param user_id: user id to fetch by
649 :param api_key: api key to fetch by
649 :param api_key: api key to fetch by
650 :param username: username to fetch by
650 :param username: username to fetch by
651 """
651 """
652 if user_id is None and api_key is None and username is None:
652 if user_id is None and api_key is None and username is None:
653 raise Exception('You need to pass user_id, api_key or username')
653 raise Exception('You need to pass user_id, api_key or username')
654
654
655 log.debug(
655 log.debug(
656 'doing fill data based on: user_id:%s api_key:%s username:%s',
656 'doing fill data based on: user_id:%s api_key:%s username:%s',
657 user_id, api_key, username)
657 user_id, api_key, username)
658 try:
658 try:
659 dbuser = None
659 dbuser = None
660 if user_id:
660 if user_id:
661 dbuser = self.get(user_id)
661 dbuser = self.get(user_id)
662 elif api_key:
662 elif api_key:
663 dbuser = self.get_by_auth_token(api_key)
663 dbuser = self.get_by_auth_token(api_key)
664 elif username:
664 elif username:
665 dbuser = self.get_by_username(username)
665 dbuser = self.get_by_username(username)
666
666
667 if not dbuser:
667 if not dbuser:
668 log.warning(
668 log.warning(
669 'Unable to lookup user by id:%s api_key:%s username:%s',
669 'Unable to lookup user by id:%s api_key:%s username:%s',
670 user_id, api_key, username)
670 user_id, api_key, username)
671 return False
671 return False
672 if not dbuser.active:
672 if not dbuser.active:
673 log.debug('User `%s:%s` is inactive, skipping fill data',
673 log.debug('User `%s:%s` is inactive, skipping fill data',
674 username, user_id)
674 username, user_id)
675 return False
675 return False
676
676
677 log.debug('filling user:%s data', dbuser)
677 log.debug('filling user:%s data', dbuser)
678
678
679 # TODO: johbo: Think about this and find a clean solution
679 # TODO: johbo: Think about this and find a clean solution
680 user_data = dbuser.get_dict()
680 user_data = dbuser.get_dict()
681 user_data.update(dbuser.get_api_data(include_secrets=True))
681 user_data.update(dbuser.get_api_data(include_secrets=True))
682
682
683 for k, v in user_data.iteritems():
683 for k, v in user_data.iteritems():
684 # properties of auth user we dont update
684 # properties of auth user we dont update
685 if k not in ['auth_tokens', 'permissions']:
685 if k not in ['auth_tokens', 'permissions']:
686 setattr(auth_user, k, v)
686 setattr(auth_user, k, v)
687
687
688 # few extras
688 # few extras
689 setattr(auth_user, 'feed_token', dbuser.feed_token)
689 setattr(auth_user, 'feed_token', dbuser.feed_token)
690 except Exception:
690 except Exception:
691 log.error(traceback.format_exc())
691 log.error(traceback.format_exc())
692 auth_user.is_authenticated = False
692 auth_user.is_authenticated = False
693 return False
693 return False
694
694
695 return True
695 return True
696
696
697 def has_perm(self, user, perm):
697 def has_perm(self, user, perm):
698 perm = self._get_perm(perm)
698 perm = self._get_perm(perm)
699 user = self._get_user(user)
699 user = self._get_user(user)
700
700
701 return UserToPerm.query().filter(UserToPerm.user == user)\
701 return UserToPerm.query().filter(UserToPerm.user == user)\
702 .filter(UserToPerm.permission == perm).scalar() is not None
702 .filter(UserToPerm.permission == perm).scalar() is not None
703
703
704 def grant_perm(self, user, perm):
704 def grant_perm(self, user, perm):
705 """
705 """
706 Grant user global permissions
706 Grant user global permissions
707
707
708 :param user:
708 :param user:
709 :param perm:
709 :param perm:
710 """
710 """
711 user = self._get_user(user)
711 user = self._get_user(user)
712 perm = self._get_perm(perm)
712 perm = self._get_perm(perm)
713 # if this permission is already granted skip it
713 # if this permission is already granted skip it
714 _perm = UserToPerm.query()\
714 _perm = UserToPerm.query()\
715 .filter(UserToPerm.user == user)\
715 .filter(UserToPerm.user == user)\
716 .filter(UserToPerm.permission == perm)\
716 .filter(UserToPerm.permission == perm)\
717 .scalar()
717 .scalar()
718 if _perm:
718 if _perm:
719 return
719 return
720 new = UserToPerm()
720 new = UserToPerm()
721 new.user = user
721 new.user = user
722 new.permission = perm
722 new.permission = perm
723 self.sa.add(new)
723 self.sa.add(new)
724 return new
724 return new
725
725
726 def revoke_perm(self, user, perm):
726 def revoke_perm(self, user, perm):
727 """
727 """
728 Revoke users global permissions
728 Revoke users global permissions
729
729
730 :param user:
730 :param user:
731 :param perm:
731 :param perm:
732 """
732 """
733 user = self._get_user(user)
733 user = self._get_user(user)
734 perm = self._get_perm(perm)
734 perm = self._get_perm(perm)
735
735
736 obj = UserToPerm.query()\
736 obj = UserToPerm.query()\
737 .filter(UserToPerm.user == user)\
737 .filter(UserToPerm.user == user)\
738 .filter(UserToPerm.permission == perm)\
738 .filter(UserToPerm.permission == perm)\
739 .scalar()
739 .scalar()
740 if obj:
740 if obj:
741 self.sa.delete(obj)
741 self.sa.delete(obj)
742
742
743 def add_extra_email(self, user, email):
743 def add_extra_email(self, user, email):
744 """
744 """
745 Adds email address to UserEmailMap
745 Adds email address to UserEmailMap
746
746
747 :param user:
747 :param user:
748 :param email:
748 :param email:
749 """
749 """
750 from rhodecode.model import forms
750 from rhodecode.model import forms
751 form = forms.UserExtraEmailForm()()
751 form = forms.UserExtraEmailForm()()
752 data = form.to_python({'email': email})
752 data = form.to_python({'email': email})
753 user = self._get_user(user)
753 user = self._get_user(user)
754
754
755 obj = UserEmailMap()
755 obj = UserEmailMap()
756 obj.user = user
756 obj.user = user
757 obj.email = data['email']
757 obj.email = data['email']
758 self.sa.add(obj)
758 self.sa.add(obj)
759 return obj
759 return obj
760
760
761 def delete_extra_email(self, user, email_id):
761 def delete_extra_email(self, user, email_id):
762 """
762 """
763 Removes email address from UserEmailMap
763 Removes email address from UserEmailMap
764
764
765 :param user:
765 :param user:
766 :param email_id:
766 :param email_id:
767 """
767 """
768 user = self._get_user(user)
768 user = self._get_user(user)
769 obj = UserEmailMap.query().get(email_id)
769 obj = UserEmailMap.query().get(email_id)
770 if obj:
770 if obj:
771 self.sa.delete(obj)
771 self.sa.delete(obj)
772
772
773 def parse_ip_range(self, ip_range):
773 def parse_ip_range(self, ip_range):
774 ip_list = []
774 ip_list = []
775 def make_unique(value):
775 def make_unique(value):
776 seen = []
776 seen = []
777 return [c for c in value if not (c in seen or seen.append(c))]
777 return [c for c in value if not (c in seen or seen.append(c))]
778
778
779 # firsts split by commas
779 # firsts split by commas
780 for ip_range in ip_range.split(','):
780 for ip_range in ip_range.split(','):
781 if not ip_range:
781 if not ip_range:
782 continue
782 continue
783 ip_range = ip_range.strip()
783 ip_range = ip_range.strip()
784 if '-' in ip_range:
784 if '-' in ip_range:
785 start_ip, end_ip = ip_range.split('-', 1)
785 start_ip, end_ip = ip_range.split('-', 1)
786 start_ip = ipaddress.ip_address(start_ip.strip())
786 start_ip = ipaddress.ip_address(start_ip.strip())
787 end_ip = ipaddress.ip_address(end_ip.strip())
787 end_ip = ipaddress.ip_address(end_ip.strip())
788 parsed_ip_range = []
788 parsed_ip_range = []
789
789
790 for index in xrange(int(start_ip), int(end_ip) + 1):
790 for index in xrange(int(start_ip), int(end_ip) + 1):
791 new_ip = ipaddress.ip_address(index)
791 new_ip = ipaddress.ip_address(index)
792 parsed_ip_range.append(str(new_ip))
792 parsed_ip_range.append(str(new_ip))
793 ip_list.extend(parsed_ip_range)
793 ip_list.extend(parsed_ip_range)
794 else:
794 else:
795 ip_list.append(ip_range)
795 ip_list.append(ip_range)
796
796
797 return make_unique(ip_list)
797 return make_unique(ip_list)
798
798
799 def add_extra_ip(self, user, ip, description=None):
799 def add_extra_ip(self, user, ip, description=None):
800 """
800 """
801 Adds ip address to UserIpMap
801 Adds ip address to UserIpMap
802
802
803 :param user:
803 :param user:
804 :param ip:
804 :param ip:
805 """
805 """
806 from rhodecode.model import forms
806 from rhodecode.model import forms
807 form = forms.UserExtraIpForm()()
807 form = forms.UserExtraIpForm()()
808 data = form.to_python({'ip': ip})
808 data = form.to_python({'ip': ip})
809 user = self._get_user(user)
809 user = self._get_user(user)
810
810
811 obj = UserIpMap()
811 obj = UserIpMap()
812 obj.user = user
812 obj.user = user
813 obj.ip_addr = data['ip']
813 obj.ip_addr = data['ip']
814 obj.description = description
814 obj.description = description
815 self.sa.add(obj)
815 self.sa.add(obj)
816 return obj
816 return obj
817
817
818 def delete_extra_ip(self, user, ip_id):
818 def delete_extra_ip(self, user, ip_id):
819 """
819 """
820 Removes ip address from UserIpMap
820 Removes ip address from UserIpMap
821
821
822 :param user:
822 :param user:
823 :param ip_id:
823 :param ip_id:
824 """
824 """
825 user = self._get_user(user)
825 user = self._get_user(user)
826 obj = UserIpMap.query().get(ip_id)
826 obj = UserIpMap.query().get(ip_id)
827 if obj:
827 if obj:
828 self.sa.delete(obj)
828 self.sa.delete(obj)
829
829
830 def get_accounts_in_creation_order(self, current_user=None):
830 def get_accounts_in_creation_order(self, current_user=None):
831 """
831 """
832 Get accounts in order of creation for deactivation for license limits
832 Get accounts in order of creation for deactivation for license limits
833
833
834 pick currently logged in user, and append to the list in position 0
834 pick currently logged in user, and append to the list in position 0
835 pick all super-admins in order of creation date and add it to the list
835 pick all super-admins in order of creation date and add it to the list
836 pick all other accounts in order of creation and add it to the list.
836 pick all other accounts in order of creation and add it to the list.
837
837
838 Based on that list, the last accounts can be disabled as they are
838 Based on that list, the last accounts can be disabled as they are
839 created at the end and don't include any of the super admins as well
839 created at the end and don't include any of the super admins as well
840 as the current user.
840 as the current user.
841
841
842 :param current_user: optionally current user running this operation
842 :param current_user: optionally current user running this operation
843 """
843 """
844
844
845 if not current_user:
845 if not current_user:
846 current_user = get_current_rhodecode_user()
846 current_user = get_current_rhodecode_user()
847 active_super_admins = [
847 active_super_admins = [
848 x.user_id for x in User.query()
848 x.user_id for x in User.query()
849 .filter(User.user_id != current_user.user_id)
849 .filter(User.user_id != current_user.user_id)
850 .filter(User.active == true())
850 .filter(User.active == true())
851 .filter(User.admin == true())
851 .filter(User.admin == true())
852 .order_by(User.created_on.asc())]
852 .order_by(User.created_on.asc())]
853
853
854 active_regular_users = [
854 active_regular_users = [
855 x.user_id for x in User.query()
855 x.user_id for x in User.query()
856 .filter(User.user_id != current_user.user_id)
856 .filter(User.user_id != current_user.user_id)
857 .filter(User.active == true())
857 .filter(User.active == true())
858 .filter(User.admin == false())
858 .filter(User.admin == false())
859 .order_by(User.created_on.asc())]
859 .order_by(User.created_on.asc())]
860
860
861 list_of_accounts = [current_user.user_id]
861 list_of_accounts = [current_user.user_id]
862 list_of_accounts += active_super_admins
862 list_of_accounts += active_super_admins
863 list_of_accounts += active_regular_users
863 list_of_accounts += active_regular_users
864
864
865 return list_of_accounts
865 return list_of_accounts
866
866
867 def deactivate_last_users(self, expected_users):
867 def deactivate_last_users(self, expected_users):
868 """
868 """
869 Deactivate accounts that are over the license limits.
869 Deactivate accounts that are over the license limits.
870 Algorithm of which accounts to disabled is based on the formula:
870 Algorithm of which accounts to disabled is based on the formula:
871
871
872 Get current user, then super admins in creation order, then regular
872 Get current user, then super admins in creation order, then regular
873 active users in creation order.
873 active users in creation order.
874
874
875 Using that list we mark all accounts from the end of it as inactive.
875 Using that list we mark all accounts from the end of it as inactive.
876 This way we block only latest created accounts.
876 This way we block only latest created accounts.
877
877
878 :param expected_users: list of users in special order, we deactivate
878 :param expected_users: list of users in special order, we deactivate
879 the end N ammoun of users from that list
879 the end N ammoun of users from that list
880 """
880 """
881
881
882 list_of_accounts = self.get_accounts_in_creation_order()
882 list_of_accounts = self.get_accounts_in_creation_order()
883
883
884 for acc_id in list_of_accounts[expected_users + 1:]:
884 for acc_id in list_of_accounts[expected_users + 1:]:
885 user = User.get(acc_id)
885 user = User.get(acc_id)
886 log.info('Deactivating account %s for license unlock', user)
886 log.info('Deactivating account %s for license unlock', user)
887 user.active = False
887 user.active = False
888 Session().add(user)
888 Session().add(user)
889 Session().commit()
889 Session().commit()
890
890
891 return
891 return
892
892
893 def get_user_log(self, user, filter_term):
893 def get_user_log(self, user, filter_term):
894 user_log = UserLog.query()\
894 user_log = UserLog.query()\
895 .filter(or_(UserLog.user_id == user.user_id,
895 .filter(or_(UserLog.user_id == user.user_id,
896 UserLog.username == user.username))\
896 UserLog.username == user.username))\
897 .options(joinedload(UserLog.user))\
897 .options(joinedload(UserLog.user))\
898 .options(joinedload(UserLog.repository))\
898 .options(joinedload(UserLog.repository))\
899 .order_by(UserLog.action_date.desc())
899 .order_by(UserLog.action_date.desc())
900
900
901 user_log = user_log_filter(user_log, filter_term)
901 user_log = user_log_filter(user_log, filter_term)
902 return user_log
902 return user_log
@@ -1,617 +1,618 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, safe_unicode
30 from rhodecode.lib.utils2 import safe_str, safe_unicode
31 from rhodecode.lib.exceptions import (
32 UserGroupAssignedException, RepoGroupAssignmentError)
33 from rhodecode.lib.utils2 import (
34 get_current_rhodecode_user, action_logger_generic)
31 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
32 from rhodecode.model.scm import UserGroupList
36 from rhodecode.model.scm import UserGroupList
33 from rhodecode.model.db import (
37 from rhodecode.model.db import (
34 true, func, User, UserGroupMember, UserGroup,
38 true, func, User, UserGroupMember, UserGroup,
35 UserGroupRepoToPerm, Permission, UserGroupToPerm, UserUserGroupToPerm,
39 UserGroupRepoToPerm, Permission, UserGroupToPerm, UserUserGroupToPerm,
36 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm)
40 UserGroupUserGroupToPerm, UserGroupRepoGroupToPerm)
37 from rhodecode.lib.exceptions import (
41
38 UserGroupAssignedException, RepoGroupAssignmentError)
39 from rhodecode.lib.utils2 import (
40 get_current_rhodecode_user, action_logger_generic)
41
42
42 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
43
44
44
45
45 class UserGroupModel(BaseModel):
46 class UserGroupModel(BaseModel):
46
47
47 cls = UserGroup
48 cls = UserGroup
48
49
49 def _get_user_group(self, user_group):
50 def _get_user_group(self, user_group):
50 return self._get_instance(UserGroup, user_group,
51 return self._get_instance(UserGroup, user_group,
51 callback=UserGroup.get_by_group_name)
52 callback=UserGroup.get_by_group_name)
52
53
53 def _create_default_perms(self, user_group):
54 def _create_default_perms(self, user_group):
54 # create default permission
55 # create default permission
55 default_perm = 'usergroup.read'
56 default_perm = 'usergroup.read'
56 def_user = User.get_default_user()
57 def_user = User.get_default_user()
57 for p in def_user.user_perms:
58 for p in def_user.user_perms:
58 if p.permission.permission_name.startswith('usergroup.'):
59 if p.permission.permission_name.startswith('usergroup.'):
59 default_perm = p.permission.permission_name
60 default_perm = p.permission.permission_name
60 break
61 break
61
62
62 user_group_to_perm = UserUserGroupToPerm()
63 user_group_to_perm = UserUserGroupToPerm()
63 user_group_to_perm.permission = Permission.get_by_key(default_perm)
64 user_group_to_perm.permission = Permission.get_by_key(default_perm)
64
65
65 user_group_to_perm.user_group = user_group
66 user_group_to_perm.user_group = user_group
66 user_group_to_perm.user_id = def_user.user_id
67 user_group_to_perm.user_id = def_user.user_id
67 return user_group_to_perm
68 return user_group_to_perm
68
69
69 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
70 def update_permissions(self, user_group, perm_additions=None, perm_updates=None,
70 perm_deletions=None, check_perms=True, cur_user=None):
71 perm_deletions=None, check_perms=True, cur_user=None):
71 from rhodecode.lib.auth import HasUserGroupPermissionAny
72 from rhodecode.lib.auth import HasUserGroupPermissionAny
72 if not perm_additions:
73 if not perm_additions:
73 perm_additions = []
74 perm_additions = []
74 if not perm_updates:
75 if not perm_updates:
75 perm_updates = []
76 perm_updates = []
76 if not perm_deletions:
77 if not perm_deletions:
77 perm_deletions = []
78 perm_deletions = []
78
79
79 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
80 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
80
81
81 # update permissions
82 # update permissions
82 for member_id, perm, member_type in perm_updates:
83 for member_id, perm, member_type in perm_updates:
83 member_id = int(member_id)
84 member_id = int(member_id)
84 if member_type == 'user':
85 if member_type == 'user':
85 # this updates existing one
86 # this updates existing one
86 self.grant_user_permission(
87 self.grant_user_permission(
87 user_group=user_group, user=member_id, perm=perm
88 user_group=user_group, user=member_id, perm=perm
88 )
89 )
89 else:
90 else:
90 # check if we have permissions to alter this usergroup
91 # check if we have permissions to alter this usergroup
91 member_name = UserGroup.get(member_id).users_group_name
92 member_name = UserGroup.get(member_id).users_group_name
92 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
93 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
93 self.grant_user_group_permission(
94 self.grant_user_group_permission(
94 target_user_group=user_group, user_group=member_id, perm=perm
95 target_user_group=user_group, user_group=member_id, perm=perm
95 )
96 )
96
97
97 # set new permissions
98 # set new permissions
98 for member_id, perm, member_type in perm_additions:
99 for member_id, perm, member_type in perm_additions:
99 member_id = int(member_id)
100 member_id = int(member_id)
100 if member_type == 'user':
101 if member_type == 'user':
101 self.grant_user_permission(
102 self.grant_user_permission(
102 user_group=user_group, user=member_id, perm=perm
103 user_group=user_group, user=member_id, perm=perm
103 )
104 )
104 else:
105 else:
105 # check if we have permissions to alter this usergroup
106 # check if we have permissions to alter this usergroup
106 member_name = UserGroup.get(member_id).users_group_name
107 member_name = UserGroup.get(member_id).users_group_name
107 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
108 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
108 self.grant_user_group_permission(
109 self.grant_user_group_permission(
109 target_user_group=user_group, user_group=member_id, perm=perm
110 target_user_group=user_group, user_group=member_id, perm=perm
110 )
111 )
111
112
112 # delete permissions
113 # delete permissions
113 for member_id, perm, member_type in perm_deletions:
114 for member_id, perm, member_type in perm_deletions:
114 member_id = int(member_id)
115 member_id = int(member_id)
115 if member_type == 'user':
116 if member_type == 'user':
116 self.revoke_user_permission(user_group=user_group, user=member_id)
117 self.revoke_user_permission(user_group=user_group, user=member_id)
117 else:
118 else:
118 # check if we have permissions to alter this usergroup
119 # check if we have permissions to alter this usergroup
119 member_name = UserGroup.get(member_id).users_group_name
120 member_name = UserGroup.get(member_id).users_group_name
120 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
121 if not check_perms or HasUserGroupPermissionAny(*req_perms)(member_name, user=cur_user):
121 self.revoke_user_group_permission(
122 self.revoke_user_group_permission(
122 target_user_group=user_group, user_group=member_id
123 target_user_group=user_group, user_group=member_id
123 )
124 )
124
125
125 def get(self, user_group_id, cache=False):
126 def get(self, user_group_id, cache=False):
126 return UserGroup.get(user_group_id)
127 return UserGroup.get(user_group_id)
127
128
128 def get_group(self, user_group):
129 def get_group(self, user_group):
129 return self._get_user_group(user_group)
130 return self._get_user_group(user_group)
130
131
131 def get_by_name(self, name, cache=False, case_insensitive=False):
132 def get_by_name(self, name, cache=False, case_insensitive=False):
132 return UserGroup.get_by_group_name(name, cache, case_insensitive)
133 return UserGroup.get_by_group_name(name, cache, case_insensitive)
133
134
134 def create(self, name, description, owner, active=True, group_data=None):
135 def create(self, name, description, owner, active=True, group_data=None):
135 try:
136 try:
136 new_user_group = UserGroup()
137 new_user_group = UserGroup()
137 new_user_group.user = self._get_user(owner)
138 new_user_group.user = self._get_user(owner)
138 new_user_group.users_group_name = name
139 new_user_group.users_group_name = name
139 new_user_group.user_group_description = description
140 new_user_group.user_group_description = description
140 new_user_group.users_group_active = active
141 new_user_group.users_group_active = active
141 if group_data:
142 if group_data:
142 new_user_group.group_data = group_data
143 new_user_group.group_data = group_data
143 self.sa.add(new_user_group)
144 self.sa.add(new_user_group)
144 perm_obj = self._create_default_perms(new_user_group)
145 perm_obj = self._create_default_perms(new_user_group)
145 self.sa.add(perm_obj)
146 self.sa.add(perm_obj)
146
147
147 self.grant_user_permission(user_group=new_user_group,
148 self.grant_user_permission(user_group=new_user_group,
148 user=owner, perm='usergroup.admin')
149 user=owner, perm='usergroup.admin')
149
150
150 return new_user_group
151 return new_user_group
151 except Exception:
152 except Exception:
152 log.error(traceback.format_exc())
153 log.error(traceback.format_exc())
153 raise
154 raise
154
155
155 def _get_memberships_for_user_ids(self, user_group, user_id_list):
156 def _get_memberships_for_user_ids(self, user_group, user_id_list):
156 members = []
157 members = []
157 for user_id in user_id_list:
158 for user_id in user_id_list:
158 member = self._get_membership(user_group.users_group_id, user_id)
159 member = self._get_membership(user_group.users_group_id, user_id)
159 members.append(member)
160 members.append(member)
160 return members
161 return members
161
162
162 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
163 def _get_added_and_removed_user_ids(self, user_group, user_id_list):
163 current_members = user_group.members or []
164 current_members = user_group.members or []
164 current_members_ids = [m.user.user_id for m in current_members]
165 current_members_ids = [m.user.user_id for m in current_members]
165
166
166 added_members = [
167 added_members = [
167 user_id for user_id in user_id_list
168 user_id for user_id in user_id_list
168 if user_id not in current_members_ids]
169 if user_id not in current_members_ids]
169 if user_id_list == []:
170 if user_id_list == []:
170 # all members were deleted
171 # all members were deleted
171 deleted_members = current_members_ids
172 deleted_members = current_members_ids
172 else:
173 else:
173 deleted_members = [
174 deleted_members = [
174 user_id for user_id in current_members_ids
175 user_id for user_id in current_members_ids
175 if user_id not in user_id_list]
176 if user_id not in user_id_list]
176
177
177 return (added_members, deleted_members)
178 return (added_members, deleted_members)
178
179
179 def _set_users_as_members(self, user_group, user_ids):
180 def _set_users_as_members(self, user_group, user_ids):
180 user_group.members = []
181 user_group.members = []
181 self.sa.flush()
182 self.sa.flush()
182 members = self._get_memberships_for_user_ids(
183 members = self._get_memberships_for_user_ids(
183 user_group, user_ids)
184 user_group, user_ids)
184 user_group.members = members
185 user_group.members = members
185 self.sa.add(user_group)
186 self.sa.add(user_group)
186
187
187 def _update_members_from_user_ids(self, user_group, user_ids):
188 def _update_members_from_user_ids(self, user_group, user_ids):
188 added, removed = self._get_added_and_removed_user_ids(
189 added, removed = self._get_added_and_removed_user_ids(
189 user_group, user_ids)
190 user_group, user_ids)
190 self._set_users_as_members(user_group, user_ids)
191 self._set_users_as_members(user_group, user_ids)
191 self._log_user_changes('added to', user_group, added)
192 self._log_user_changes('added to', user_group, added)
192 self._log_user_changes('removed from', user_group, removed)
193 self._log_user_changes('removed from', user_group, removed)
193
194
194 def _clean_members_data(self, members_data):
195 def _clean_members_data(self, members_data):
195 if not members_data:
196 if not members_data:
196 members_data = []
197 members_data = []
197
198
198 members = []
199 members = []
199 for user in members_data:
200 for user in members_data:
200 uid = int(user['member_user_id'])
201 uid = int(user['member_user_id'])
201 if uid not in members and user['type'] in ['new', 'existing']:
202 if uid not in members and user['type'] in ['new', 'existing']:
202 members.append(uid)
203 members.append(uid)
203 return members
204 return members
204
205
205 def update(self, user_group, form_data):
206 def update(self, user_group, form_data):
206 user_group = self._get_user_group(user_group)
207 user_group = self._get_user_group(user_group)
207 if 'users_group_name' in form_data:
208 if 'users_group_name' in form_data:
208 user_group.users_group_name = form_data['users_group_name']
209 user_group.users_group_name = form_data['users_group_name']
209 if 'users_group_active' in form_data:
210 if 'users_group_active' in form_data:
210 user_group.users_group_active = form_data['users_group_active']
211 user_group.users_group_active = form_data['users_group_active']
211 if 'user_group_description' in form_data:
212 if 'user_group_description' in form_data:
212 user_group.user_group_description = form_data[
213 user_group.user_group_description = form_data[
213 'user_group_description']
214 'user_group_description']
214
215
215 # handle owner change
216 # handle owner change
216 if 'user' in form_data:
217 if 'user' in form_data:
217 owner = form_data['user']
218 owner = form_data['user']
218 if isinstance(owner, basestring):
219 if isinstance(owner, basestring):
219 owner = User.get_by_username(form_data['user'])
220 owner = User.get_by_username(form_data['user'])
220
221
221 if not isinstance(owner, User):
222 if not isinstance(owner, User):
222 raise ValueError(
223 raise ValueError(
223 'invalid owner for user group: %s' % form_data['user'])
224 'invalid owner for user group: %s' % form_data['user'])
224
225
225 user_group.user = owner
226 user_group.user = owner
226
227
227 if 'users_group_members' in form_data:
228 if 'users_group_members' in form_data:
228 members_id_list = self._clean_members_data(
229 members_id_list = self._clean_members_data(
229 form_data['users_group_members'])
230 form_data['users_group_members'])
230 self._update_members_from_user_ids(user_group, members_id_list)
231 self._update_members_from_user_ids(user_group, members_id_list)
231
232
232 self.sa.add(user_group)
233 self.sa.add(user_group)
233
234
234 def delete(self, user_group, force=False):
235 def delete(self, user_group, force=False):
235 """
236 """
236 Deletes repository group, unless force flag is used
237 Deletes repository group, unless force flag is used
237 raises exception if there are members in that group, else deletes
238 raises exception if there are members in that group, else deletes
238 group and users
239 group and users
239
240
240 :param user_group:
241 :param user_group:
241 :param force:
242 :param force:
242 """
243 """
243 user_group = self._get_user_group(user_group)
244 user_group = self._get_user_group(user_group)
244 try:
245 try:
245 # check if this group is not assigned to repo
246 # check if this group is not assigned to repo
246 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
247 assigned_to_repo = [x.repository for x in UserGroupRepoToPerm.query()\
247 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
248 .filter(UserGroupRepoToPerm.users_group == user_group).all()]
248 # check if this group is not assigned to repo
249 # check if this group is not assigned to repo
249 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
250 assigned_to_repo_group = [x.group for x in UserGroupRepoGroupToPerm.query()\
250 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
251 .filter(UserGroupRepoGroupToPerm.users_group == user_group).all()]
251
252
252 if (assigned_to_repo or assigned_to_repo_group) and not force:
253 if (assigned_to_repo or assigned_to_repo_group) and not force:
253 assigned = ','.join(map(safe_str,
254 assigned = ','.join(map(safe_str,
254 assigned_to_repo+assigned_to_repo_group))
255 assigned_to_repo+assigned_to_repo_group))
255
256
256 raise UserGroupAssignedException(
257 raise UserGroupAssignedException(
257 'UserGroup assigned to %s' % (assigned,))
258 'UserGroup assigned to %s' % (assigned,))
258 self.sa.delete(user_group)
259 self.sa.delete(user_group)
259 except Exception:
260 except Exception:
260 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
261 raise
262 raise
262
263
263 def _log_user_changes(self, action, user_group, user_or_users):
264 def _log_user_changes(self, action, user_group, user_or_users):
264 users = user_or_users
265 users = user_or_users
265 if not isinstance(users, (list, tuple)):
266 if not isinstance(users, (list, tuple)):
266 users = [users]
267 users = [users]
267 rhodecode_user = get_current_rhodecode_user()
268 rhodecode_user = get_current_rhodecode_user()
268 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
269 ipaddr = getattr(rhodecode_user, 'ip_addr', '')
269 group_name = user_group.users_group_name
270 group_name = user_group.users_group_name
270
271
271 for user_or_user_id in users:
272 for user_or_user_id in users:
272 user = self._get_user(user_or_user_id)
273 user = self._get_user(user_or_user_id)
273 log_text = 'User {user} {action} {group}'.format(
274 log_text = 'User {user} {action} {group}'.format(
274 action=action, user=user.username, group=group_name)
275 action=action, user=user.username, group=group_name)
275 log.info('Logging action: {0} by {1} ip:{2}'.format(
276 log.info('Logging action: {0} by {1} ip:{2}'.format(
276 log_text, rhodecode_user, ipaddr))
277 log_text, rhodecode_user, ipaddr))
277
278
278 def _find_user_in_group(self, user, user_group):
279 def _find_user_in_group(self, user, user_group):
279 user_group_member = None
280 user_group_member = None
280 for m in user_group.members:
281 for m in user_group.members:
281 if m.user_id == user.user_id:
282 if m.user_id == user.user_id:
282 # Found this user's membership row
283 # Found this user's membership row
283 user_group_member = m
284 user_group_member = m
284 break
285 break
285
286
286 return user_group_member
287 return user_group_member
287
288
288 def _get_membership(self, user_group_id, user_id):
289 def _get_membership(self, user_group_id, user_id):
289 user_group_member = UserGroupMember(user_group_id, user_id)
290 user_group_member = UserGroupMember(user_group_id, user_id)
290 return user_group_member
291 return user_group_member
291
292
292 def add_user_to_group(self, user_group, user):
293 def add_user_to_group(self, user_group, user):
293 user_group = self._get_user_group(user_group)
294 user_group = self._get_user_group(user_group)
294 user = self._get_user(user)
295 user = self._get_user(user)
295 user_member = self._find_user_in_group(user, user_group)
296 user_member = self._find_user_in_group(user, user_group)
296 if user_member:
297 if user_member:
297 # user already in the group, skip
298 # user already in the group, skip
298 return True
299 return True
299
300
300 member = self._get_membership(
301 member = self._get_membership(
301 user_group.users_group_id, user.user_id)
302 user_group.users_group_id, user.user_id)
302 user_group.members.append(member)
303 user_group.members.append(member)
303
304
304 try:
305 try:
305 self.sa.add(member)
306 self.sa.add(member)
306 except Exception:
307 except Exception:
307 # what could go wrong here?
308 # what could go wrong here?
308 log.error(traceback.format_exc())
309 log.error(traceback.format_exc())
309 raise
310 raise
310
311
311 self._log_user_changes('added to', user_group, user)
312 self._log_user_changes('added to', user_group, user)
312 return member
313 return member
313
314
314 def remove_user_from_group(self, user_group, user):
315 def remove_user_from_group(self, user_group, user):
315 user_group = self._get_user_group(user_group)
316 user_group = self._get_user_group(user_group)
316 user = self._get_user(user)
317 user = self._get_user(user)
317 user_group_member = self._find_user_in_group(user, user_group)
318 user_group_member = self._find_user_in_group(user, user_group)
318
319
319 if not user_group_member:
320 if not user_group_member:
320 # User isn't in that group
321 # User isn't in that group
321 return False
322 return False
322
323
323 try:
324 try:
324 self.sa.delete(user_group_member)
325 self.sa.delete(user_group_member)
325 except Exception:
326 except Exception:
326 log.error(traceback.format_exc())
327 log.error(traceback.format_exc())
327 raise
328 raise
328
329
329 self._log_user_changes('removed from', user_group, user)
330 self._log_user_changes('removed from', user_group, user)
330 return True
331 return True
331
332
332 def has_perm(self, user_group, perm):
333 def has_perm(self, user_group, perm):
333 user_group = self._get_user_group(user_group)
334 user_group = self._get_user_group(user_group)
334 perm = self._get_perm(perm)
335 perm = self._get_perm(perm)
335
336
336 return UserGroupToPerm.query()\
337 return UserGroupToPerm.query()\
337 .filter(UserGroupToPerm.users_group == user_group)\
338 .filter(UserGroupToPerm.users_group == user_group)\
338 .filter(UserGroupToPerm.permission == perm).scalar() is not None
339 .filter(UserGroupToPerm.permission == perm).scalar() is not None
339
340
340 def grant_perm(self, user_group, perm):
341 def grant_perm(self, user_group, perm):
341 user_group = self._get_user_group(user_group)
342 user_group = self._get_user_group(user_group)
342 perm = self._get_perm(perm)
343 perm = self._get_perm(perm)
343
344
344 # if this permission is already granted skip it
345 # if this permission is already granted skip it
345 _perm = UserGroupToPerm.query()\
346 _perm = UserGroupToPerm.query()\
346 .filter(UserGroupToPerm.users_group == user_group)\
347 .filter(UserGroupToPerm.users_group == user_group)\
347 .filter(UserGroupToPerm.permission == perm)\
348 .filter(UserGroupToPerm.permission == perm)\
348 .scalar()
349 .scalar()
349 if _perm:
350 if _perm:
350 return
351 return
351
352
352 new = UserGroupToPerm()
353 new = UserGroupToPerm()
353 new.users_group = user_group
354 new.users_group = user_group
354 new.permission = perm
355 new.permission = perm
355 self.sa.add(new)
356 self.sa.add(new)
356 return new
357 return new
357
358
358 def revoke_perm(self, user_group, perm):
359 def revoke_perm(self, user_group, perm):
359 user_group = self._get_user_group(user_group)
360 user_group = self._get_user_group(user_group)
360 perm = self._get_perm(perm)
361 perm = self._get_perm(perm)
361
362
362 obj = UserGroupToPerm.query()\
363 obj = UserGroupToPerm.query()\
363 .filter(UserGroupToPerm.users_group == user_group)\
364 .filter(UserGroupToPerm.users_group == user_group)\
364 .filter(UserGroupToPerm.permission == perm).scalar()
365 .filter(UserGroupToPerm.permission == perm).scalar()
365 if obj:
366 if obj:
366 self.sa.delete(obj)
367 self.sa.delete(obj)
367
368
368 def grant_user_permission(self, user_group, user, perm):
369 def grant_user_permission(self, user_group, user, perm):
369 """
370 """
370 Grant permission for user on given user group, or update
371 Grant permission for user on given user group, or update
371 existing one if found
372 existing one if found
372
373
373 :param user_group: Instance of UserGroup, users_group_id,
374 :param user_group: Instance of UserGroup, users_group_id,
374 or users_group_name
375 or users_group_name
375 :param user: Instance of User, user_id or username
376 :param user: Instance of User, user_id or username
376 :param perm: Instance of Permission, or permission_name
377 :param perm: Instance of Permission, or permission_name
377 """
378 """
378
379
379 user_group = self._get_user_group(user_group)
380 user_group = self._get_user_group(user_group)
380 user = self._get_user(user)
381 user = self._get_user(user)
381 permission = self._get_perm(perm)
382 permission = self._get_perm(perm)
382
383
383 # check if we have that permission already
384 # check if we have that permission already
384 obj = self.sa.query(UserUserGroupToPerm)\
385 obj = self.sa.query(UserUserGroupToPerm)\
385 .filter(UserUserGroupToPerm.user == user)\
386 .filter(UserUserGroupToPerm.user == user)\
386 .filter(UserUserGroupToPerm.user_group == user_group)\
387 .filter(UserUserGroupToPerm.user_group == user_group)\
387 .scalar()
388 .scalar()
388 if obj is None:
389 if obj is None:
389 # create new !
390 # create new !
390 obj = UserUserGroupToPerm()
391 obj = UserUserGroupToPerm()
391 obj.user_group = user_group
392 obj.user_group = user_group
392 obj.user = user
393 obj.user = user
393 obj.permission = permission
394 obj.permission = permission
394 self.sa.add(obj)
395 self.sa.add(obj)
395 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
396 log.debug('Granted perm %s to %s on %s', perm, user, user_group)
396 action_logger_generic(
397 action_logger_generic(
397 'granted permission: {} to user: {} on usergroup: {}'.format(
398 'granted permission: {} to user: {} on usergroup: {}'.format(
398 perm, user, user_group), namespace='security.usergroup')
399 perm, user, user_group), namespace='security.usergroup')
399
400
400 return obj
401 return obj
401
402
402 def revoke_user_permission(self, user_group, user):
403 def revoke_user_permission(self, user_group, user):
403 """
404 """
404 Revoke permission for user on given user group
405 Revoke permission for user on given user group
405
406
406 :param user_group: Instance of UserGroup, users_group_id,
407 :param user_group: Instance of UserGroup, users_group_id,
407 or users_group name
408 or users_group name
408 :param user: Instance of User, user_id or username
409 :param user: Instance of User, user_id or username
409 """
410 """
410
411
411 user_group = self._get_user_group(user_group)
412 user_group = self._get_user_group(user_group)
412 user = self._get_user(user)
413 user = self._get_user(user)
413
414
414 obj = self.sa.query(UserUserGroupToPerm)\
415 obj = self.sa.query(UserUserGroupToPerm)\
415 .filter(UserUserGroupToPerm.user == user)\
416 .filter(UserUserGroupToPerm.user == user)\
416 .filter(UserUserGroupToPerm.user_group == user_group)\
417 .filter(UserUserGroupToPerm.user_group == user_group)\
417 .scalar()
418 .scalar()
418 if obj:
419 if obj:
419 self.sa.delete(obj)
420 self.sa.delete(obj)
420 log.debug('Revoked perm on %s on %s', user_group, user)
421 log.debug('Revoked perm on %s on %s', user_group, user)
421 action_logger_generic(
422 action_logger_generic(
422 'revoked permission from user: {} on usergroup: {}'.format(
423 'revoked permission from user: {} on usergroup: {}'.format(
423 user, user_group), namespace='security.usergroup')
424 user, user_group), namespace='security.usergroup')
424
425
425 def grant_user_group_permission(self, target_user_group, user_group, perm):
426 def grant_user_group_permission(self, target_user_group, user_group, perm):
426 """
427 """
427 Grant user group permission for given target_user_group
428 Grant user group permission for given target_user_group
428
429
429 :param target_user_group:
430 :param target_user_group:
430 :param user_group:
431 :param user_group:
431 :param perm:
432 :param perm:
432 """
433 """
433 target_user_group = self._get_user_group(target_user_group)
434 target_user_group = self._get_user_group(target_user_group)
434 user_group = self._get_user_group(user_group)
435 user_group = self._get_user_group(user_group)
435 permission = self._get_perm(perm)
436 permission = self._get_perm(perm)
436 # forbid assigning same user group to itself
437 # forbid assigning same user group to itself
437 if target_user_group == user_group:
438 if target_user_group == user_group:
438 raise RepoGroupAssignmentError('target repo:%s cannot be '
439 raise RepoGroupAssignmentError('target repo:%s cannot be '
439 'assigned to itself' % target_user_group)
440 'assigned to itself' % target_user_group)
440
441
441 # check if we have that permission already
442 # check if we have that permission already
442 obj = self.sa.query(UserGroupUserGroupToPerm)\
443 obj = self.sa.query(UserGroupUserGroupToPerm)\
443 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
444 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
444 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
445 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
445 .scalar()
446 .scalar()
446 if obj is None:
447 if obj is None:
447 # create new !
448 # create new !
448 obj = UserGroupUserGroupToPerm()
449 obj = UserGroupUserGroupToPerm()
449 obj.user_group = user_group
450 obj.user_group = user_group
450 obj.target_user_group = target_user_group
451 obj.target_user_group = target_user_group
451 obj.permission = permission
452 obj.permission = permission
452 self.sa.add(obj)
453 self.sa.add(obj)
453 log.debug(
454 log.debug(
454 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
455 'Granted perm %s to %s on %s', perm, target_user_group, user_group)
455 action_logger_generic(
456 action_logger_generic(
456 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
457 'granted permission: {} to usergroup: {} on usergroup: {}'.format(
457 perm, user_group, target_user_group),
458 perm, user_group, target_user_group),
458 namespace='security.usergroup')
459 namespace='security.usergroup')
459
460
460 return obj
461 return obj
461
462
462 def revoke_user_group_permission(self, target_user_group, user_group):
463 def revoke_user_group_permission(self, target_user_group, user_group):
463 """
464 """
464 Revoke user group permission for given target_user_group
465 Revoke user group permission for given target_user_group
465
466
466 :param target_user_group:
467 :param target_user_group:
467 :param user_group:
468 :param user_group:
468 """
469 """
469 target_user_group = self._get_user_group(target_user_group)
470 target_user_group = self._get_user_group(target_user_group)
470 user_group = self._get_user_group(user_group)
471 user_group = self._get_user_group(user_group)
471
472
472 obj = self.sa.query(UserGroupUserGroupToPerm)\
473 obj = self.sa.query(UserGroupUserGroupToPerm)\
473 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
474 .filter(UserGroupUserGroupToPerm.target_user_group == target_user_group)\
474 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
475 .filter(UserGroupUserGroupToPerm.user_group == user_group)\
475 .scalar()
476 .scalar()
476 if obj:
477 if obj:
477 self.sa.delete(obj)
478 self.sa.delete(obj)
478 log.debug(
479 log.debug(
479 'Revoked perm on %s on %s', target_user_group, user_group)
480 'Revoked perm on %s on %s', target_user_group, user_group)
480 action_logger_generic(
481 action_logger_generic(
481 'revoked permission from usergroup: {} on usergroup: {}'.format(
482 'revoked permission from usergroup: {} on usergroup: {}'.format(
482 user_group, target_user_group),
483 user_group, target_user_group),
483 namespace='security.repogroup')
484 namespace='security.repogroup')
484
485
485 def enforce_groups(self, user, groups, extern_type=None):
486 def enforce_groups(self, user, groups, extern_type=None):
486 user = self._get_user(user)
487 user = self._get_user(user)
487 log.debug('Enforcing groups %s on user %s', groups, user)
488 log.debug('Enforcing groups %s on user %s', groups, user)
488 current_groups = user.group_member
489 current_groups = user.group_member
489 # find the external created groups
490 # find the external created groups
490 externals = [x.users_group for x in current_groups
491 externals = [x.users_group for x in current_groups
491 if 'extern_type' in x.users_group.group_data]
492 if 'extern_type' in x.users_group.group_data]
492
493
493 # calculate from what groups user should be removed
494 # calculate from what groups user should be removed
494 # externals that are not in groups
495 # externals that are not in groups
495 for gr in externals:
496 for gr in externals:
496 if gr.users_group_name not in groups:
497 if gr.users_group_name not in groups:
497 log.debug('Removing user %s from user group %s', user, gr)
498 log.debug('Removing user %s from user group %s', user, gr)
498 self.remove_user_from_group(gr, user)
499 self.remove_user_from_group(gr, user)
499
500
500 # now we calculate in which groups user should be == groups params
501 # now we calculate in which groups user should be == groups params
501 owner = User.get_first_super_admin().username
502 owner = User.get_first_super_admin().username
502 for gr in set(groups):
503 for gr in set(groups):
503 existing_group = UserGroup.get_by_group_name(gr)
504 existing_group = UserGroup.get_by_group_name(gr)
504 if not existing_group:
505 if not existing_group:
505 desc = 'Automatically created from plugin:%s' % extern_type
506 desc = 'Automatically created from plugin:%s' % extern_type
506 # we use first admin account to set the owner of the group
507 # we use first admin account to set the owner of the group
507 existing_group = UserGroupModel().create(
508 existing_group = UserGroupModel().create(
508 gr, desc, owner, group_data={'extern_type': extern_type})
509 gr, desc, owner, group_data={'extern_type': extern_type})
509
510
510 # we can only add users to special groups created via plugins
511 # we can only add users to special groups created via plugins
511 managed = 'extern_type' in existing_group.group_data
512 managed = 'extern_type' in existing_group.group_data
512 if managed:
513 if managed:
513 log.debug('Adding user %s to user group %s', user, gr)
514 log.debug('Adding user %s to user group %s', user, gr)
514 UserGroupModel().add_user_to_group(existing_group, user)
515 UserGroupModel().add_user_to_group(existing_group, user)
515 else:
516 else:
516 log.debug('Skipping addition to group %s since it is '
517 log.debug('Skipping addition to group %s since it is '
517 'not set to be automatically synchronized' % gr)
518 'not set to be automatically synchronized' % gr)
518
519
519 def change_groups(self, user, groups):
520 def change_groups(self, user, groups):
520 """
521 """
521 This method changes user group assignment
522 This method changes user group assignment
522 :param user: User
523 :param user: User
523 :param groups: array of UserGroupModel
524 :param groups: array of UserGroupModel
524 :return:
525 :return:
525 """
526 """
526 user = self._get_user(user)
527 user = self._get_user(user)
527 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
528 log.debug('Changing user(%s) assignment to groups(%s)', user, groups)
528 current_groups = user.group_member
529 current_groups = user.group_member
529 current_groups = [x.users_group for x in current_groups]
530 current_groups = [x.users_group for x in current_groups]
530
531
531 # calculate from what groups user should be removed/add
532 # calculate from what groups user should be removed/add
532 groups = set(groups)
533 groups = set(groups)
533 current_groups = set(current_groups)
534 current_groups = set(current_groups)
534
535
535 groups_to_remove = current_groups - groups
536 groups_to_remove = current_groups - groups
536 groups_to_add = groups - current_groups
537 groups_to_add = groups - current_groups
537
538
538 for gr in groups_to_remove:
539 for gr in groups_to_remove:
539 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
540 log.debug('Removing user %s from user group %s', user.username, gr.users_group_name)
540 self.remove_user_from_group(gr.users_group_name, user.username)
541 self.remove_user_from_group(gr.users_group_name, user.username)
541 for gr in groups_to_add:
542 for gr in groups_to_add:
542 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
543 log.debug('Adding user %s to user group %s', user.username, gr.users_group_name)
543 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
544 UserGroupModel().add_user_to_group(gr.users_group_name, user.username)
544
545
545 def _serialize_user_group(self, user_group):
546 def _serialize_user_group(self, user_group):
546 import rhodecode.lib.helpers as h
547 import rhodecode.lib.helpers as h
547 return {
548 return {
548 'id': user_group.users_group_id,
549 'id': user_group.users_group_id,
549 # TODO: marcink figure out a way to generate the url for the
550 # TODO: marcink figure out a way to generate the url for the
550 # icon
551 # icon
551 'icon_link': '',
552 'icon_link': '',
552 'value_display': 'Group: %s (%d members)' % (
553 'value_display': 'Group: %s (%d members)' % (
553 user_group.users_group_name, len(user_group.members),),
554 user_group.users_group_name, len(user_group.members),),
554 'value': user_group.users_group_name,
555 'value': user_group.users_group_name,
555 'description': user_group.user_group_description,
556 'description': user_group.user_group_description,
556 'owner': user_group.user.username,
557 'owner': user_group.user.username,
557
558
558 'owner_icon': h.gravatar_url(user_group.user.email, 30),
559 'owner_icon': h.gravatar_url(user_group.user.email, 30),
559 'value_display_owner': h.person(user_group.user.email),
560 'value_display_owner': h.person(user_group.user.email),
560
561
561 'value_type': 'user_group',
562 'value_type': 'user_group',
562 'active': user_group.users_group_active,
563 'active': user_group.users_group_active,
563 }
564 }
564
565
565 def get_user_groups(self, name_contains=None, limit=20, only_active=True,
566 def get_user_groups(self, name_contains=None, limit=20, only_active=True,
566 expand_groups=False):
567 expand_groups=False):
567 query = self.sa.query(UserGroup)
568 query = self.sa.query(UserGroup)
568 if only_active:
569 if only_active:
569 query = query.filter(UserGroup.users_group_active == true())
570 query = query.filter(UserGroup.users_group_active == true())
570
571
571 if name_contains:
572 if name_contains:
572 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
573 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
573 query = query.filter(
574 query = query.filter(
574 UserGroup.users_group_name.ilike(ilike_expression))\
575 UserGroup.users_group_name.ilike(ilike_expression))\
575 .order_by(func.length(UserGroup.users_group_name))\
576 .order_by(func.length(UserGroup.users_group_name))\
576 .order_by(UserGroup.users_group_name)
577 .order_by(UserGroup.users_group_name)
577
578
578 query = query.limit(limit)
579 query = query.limit(limit)
579 user_groups = query.all()
580 user_groups = query.all()
580 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
581 perm_set = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
581 user_groups = UserGroupList(user_groups, perm_set=perm_set)
582 user_groups = UserGroupList(user_groups, perm_set=perm_set)
582
583
583 # store same serialize method to extract data from User
584 # store same serialize method to extract data from User
584 from rhodecode.model.user import UserModel
585 from rhodecode.model.user import UserModel
585 serialize_user = UserModel()._serialize_user
586 serialize_user = UserModel()._serialize_user
586
587
587 _groups = []
588 _groups = []
588 for group in user_groups:
589 for group in user_groups:
589 entry = self._serialize_user_group(group)
590 entry = self._serialize_user_group(group)
590 if expand_groups:
591 if expand_groups:
591 expanded_members = []
592 expanded_members = []
592 for member in group.members:
593 for member in group.members:
593 expanded_members.append(serialize_user(member.user))
594 expanded_members.append(serialize_user(member.user))
594 entry['members'] = expanded_members
595 entry['members'] = expanded_members
595 _groups.append(entry)
596 _groups.append(entry)
596 return _groups
597 return _groups
597
598
598 @staticmethod
599 @staticmethod
599 def get_user_groups_as_dict(user_group):
600 def get_user_groups_as_dict(user_group):
600 import rhodecode.lib.helpers as h
601 import rhodecode.lib.helpers as h
601
602
602 data = {
603 data = {
603 'users_group_id': user_group.users_group_id,
604 'users_group_id': user_group.users_group_id,
604 'group_name': user_group.users_group_name,
605 'group_name': user_group.users_group_name,
605 'group_description': user_group.user_group_description,
606 'group_description': user_group.user_group_description,
606 'active': user_group.users_group_active,
607 'active': user_group.users_group_active,
607 "owner": user_group.user.username,
608 "owner": user_group.user.username,
608 'owner_icon': h.gravatar_url(user_group.user.email, 30),
609 'owner_icon': h.gravatar_url(user_group.user.email, 30),
609 "owner_data": {
610 "owner_data": {
610 'owner': user_group.user.username,
611 'owner': user_group.user.username,
611 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
612 'owner_icon': h.gravatar_url(user_group.user.email, 30)}
612 }
613 }
613 return data
614 return data
614
615
615
616
616
617
617
618
General Comments 0
You need to be logged in to leave comments. Login now