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