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