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