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