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