|
@@
-1,1046
+1,1050
|
|
1
|
# -*- coding: utf-8 -*-
|
|
1
|
# -*- coding: utf-8 -*-
|
|
2
|
|
|
2
|
|
|
3
|
# Copyright (C) 2010-2020 RhodeCode GmbH
|
|
3
|
# Copyright (C) 2010-2020 RhodeCode GmbH
|
|
4
|
#
|
|
4
|
#
|
|
5
|
# This program is free software: you can redistribute it and/or modify
|
|
5
|
# This program is free software: you can redistribute it and/or modify
|
|
6
|
# it under the terms of the GNU Affero General Public License, version 3
|
|
6
|
# it under the terms of the GNU Affero General Public License, version 3
|
|
7
|
# (only), as published by the Free Software Foundation.
|
|
7
|
# (only), as published by the Free Software Foundation.
|
|
8
|
#
|
|
8
|
#
|
|
9
|
# This program is distributed in the hope that it will be useful,
|
|
9
|
# This program is distributed in the hope that it will be useful,
|
|
10
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
# GNU General Public License for more details.
|
|
12
|
# GNU General Public License for more details.
|
|
13
|
#
|
|
13
|
#
|
|
14
|
# You should have received a copy of the GNU Affero General Public License
|
|
14
|
# You should have received a copy of the GNU Affero General Public License
|
|
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
#
|
|
16
|
#
|
|
17
|
# This program is dual-licensed. If you wish to learn more about the
|
|
17
|
# This program is dual-licensed. If you wish to learn more about the
|
|
18
|
# RhodeCode Enterprise Edition, including its added features, Support services,
|
|
18
|
# RhodeCode Enterprise Edition, including its added features, Support services,
|
|
19
|
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
|
19
|
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
|
20
|
|
|
20
|
|
|
21
|
"""
|
|
21
|
"""
|
|
22
|
users model for RhodeCode
|
|
22
|
users model for RhodeCode
|
|
23
|
"""
|
|
23
|
"""
|
|
24
|
|
|
24
|
|
|
25
|
import logging
|
|
25
|
import logging
|
|
26
|
import traceback
|
|
26
|
import traceback
|
|
27
|
import datetime
|
|
27
|
import datetime
|
|
28
|
import ipaddress
|
|
28
|
import ipaddress
|
|
29
|
|
|
29
|
|
|
30
|
from pyramid.threadlocal import get_current_request
|
|
30
|
from pyramid.threadlocal import get_current_request
|
|
31
|
from sqlalchemy.exc import DatabaseError
|
|
31
|
from sqlalchemy.exc import DatabaseError
|
|
32
|
|
|
32
|
|
|
33
|
from rhodecode import events
|
|
33
|
from rhodecode import events
|
|
34
|
from rhodecode.lib.user_log_filter import user_log_filter
|
|
34
|
from rhodecode.lib.user_log_filter import user_log_filter
|
|
35
|
from rhodecode.lib.utils2 import (
|
|
35
|
from rhodecode.lib.utils2 import (
|
|
36
|
safe_unicode, get_current_rhodecode_user, action_logger_generic,
|
|
36
|
safe_unicode, get_current_rhodecode_user, action_logger_generic,
|
|
37
|
AttributeDict, str2bool)
|
|
37
|
AttributeDict, str2bool)
|
|
38
|
from rhodecode.lib.exceptions import (
|
|
38
|
from rhodecode.lib.exceptions import (
|
|
39
|
DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
|
|
39
|
DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
|
|
40
|
UserOwnsUserGroupsException, NotAllowedToCreateUserError,
|
|
40
|
UserOwnsUserGroupsException, NotAllowedToCreateUserError,
|
|
41
|
UserOwnsPullRequestsException, UserOwnsArtifactsException)
|
|
41
|
UserOwnsPullRequestsException, UserOwnsArtifactsException)
|
|
42
|
from rhodecode.lib.caching_query import FromCache
|
|
42
|
from rhodecode.lib.caching_query import FromCache
|
|
43
|
from rhodecode.model import BaseModel
|
|
43
|
from rhodecode.model import BaseModel
|
|
44
|
from rhodecode.model.db import (
|
|
44
|
from rhodecode.model.db import (
|
|
45
|
_hash_key, true, false, or_, joinedload, User, UserToPerm,
|
|
45
|
_hash_key, func, true, false, or_, joinedload, User, UserToPerm,
|
|
46
|
UserEmailMap, UserIpMap, UserLog)
|
|
46
|
UserEmailMap, UserIpMap, UserLog)
|
|
47
|
from rhodecode.model.meta import Session
|
|
47
|
from rhodecode.model.meta import Session
|
|
48
|
from rhodecode.model.auth_token import AuthTokenModel
|
|
48
|
from rhodecode.model.auth_token import AuthTokenModel
|
|
49
|
from rhodecode.model.repo_group import RepoGroupModel
|
|
49
|
from rhodecode.model.repo_group import RepoGroupModel
|
|
50
|
|
|
50
|
|
|
51
|
log = logging.getLogger(__name__)
|
|
51
|
log = logging.getLogger(__name__)
|
|
52
|
|
|
52
|
|
|
53
|
|
|
53
|
|
|
54
|
class UserModel(BaseModel):
|
|
54
|
class UserModel(BaseModel):
|
|
55
|
cls = User
|
|
55
|
cls = User
|
|
56
|
|
|
56
|
|
|
57
|
def get(self, user_id, cache=False):
|
|
57
|
def get(self, user_id, cache=False):
|
|
58
|
user = self.sa.query(User)
|
|
58
|
user = self.sa.query(User)
|
|
59
|
if cache:
|
|
59
|
if cache:
|
|
60
|
user = user.options(
|
|
60
|
user = user.options(
|
|
61
|
FromCache("sql_cache_short", "get_user_%s" % user_id))
|
|
61
|
FromCache("sql_cache_short", "get_user_%s" % user_id))
|
|
62
|
return user.get(user_id)
|
|
62
|
return user.get(user_id)
|
|
63
|
|
|
63
|
|
|
64
|
def get_user(self, user):
|
|
64
|
def get_user(self, user):
|
|
65
|
return self._get_user(user)
|
|
65
|
return self._get_user(user)
|
|
66
|
|
|
66
|
|
|
67
|
def _serialize_user(self, user):
|
|
67
|
def _serialize_user(self, user):
|
|
68
|
import rhodecode.lib.helpers as h
|
|
68
|
import rhodecode.lib.helpers as h
|
|
69
|
|
|
69
|
|
|
70
|
return {
|
|
70
|
return {
|
|
71
|
'id': user.user_id,
|
|
71
|
'id': user.user_id,
|
|
72
|
'first_name': user.first_name,
|
|
72
|
'first_name': user.first_name,
|
|
73
|
'last_name': user.last_name,
|
|
73
|
'last_name': user.last_name,
|
|
74
|
'username': user.username,
|
|
74
|
'username': user.username,
|
|
75
|
'email': user.email,
|
|
75
|
'email': user.email,
|
|
76
|
'icon_link': h.gravatar_url(user.email, 30),
|
|
76
|
'icon_link': h.gravatar_url(user.email, 30),
|
|
77
|
'profile_link': h.link_to_user(user),
|
|
77
|
'profile_link': h.link_to_user(user),
|
|
78
|
'value_display': h.escape(h.person(user)),
|
|
78
|
'value_display': h.escape(h.person(user)),
|
|
79
|
'value': user.username,
|
|
79
|
'value': user.username,
|
|
80
|
'value_type': 'user',
|
|
80
|
'value_type': 'user',
|
|
81
|
'active': user.active,
|
|
81
|
'active': user.active,
|
|
82
|
}
|
|
82
|
}
|
|
83
|
|
|
83
|
|
|
84
|
def get_users(self, name_contains=None, limit=20, only_active=True):
|
|
84
|
def get_users(self, name_contains=None, limit=20, only_active=True):
|
|
85
|
|
|
85
|
|
|
86
|
query = self.sa.query(User)
|
|
86
|
query = self.sa.query(User)
|
|
87
|
if only_active:
|
|
87
|
if only_active:
|
|
88
|
query = query.filter(User.active == true())
|
|
88
|
query = query.filter(User.active == true())
|
|
89
|
|
|
89
|
|
|
90
|
if name_contains:
|
|
90
|
if name_contains:
|
|
91
|
ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
|
|
91
|
ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
|
|
92
|
query = query.filter(
|
|
92
|
query = query.filter(
|
|
93
|
or_(
|
|
93
|
or_(
|
|
94
|
User.name.ilike(ilike_expression),
|
|
94
|
User.name.ilike(ilike_expression),
|
|
95
|
User.lastname.ilike(ilike_expression),
|
|
95
|
User.lastname.ilike(ilike_expression),
|
|
96
|
User.username.ilike(ilike_expression)
|
|
96
|
User.username.ilike(ilike_expression)
|
|
97
|
)
|
|
97
|
)
|
|
98
|
)
|
|
98
|
)
|
|
|
|
|
99
|
# sort by len to have top most matches first
|
|
|
|
|
100
|
query = query.order_by(func.length(User.username))\
|
|
|
|
|
101
|
.order_by(User.username)
|
|
99
|
query = query.limit(limit)
|
|
102
|
query = query.limit(limit)
|
|
|
|
|
103
|
|
|
100
|
users = query.all()
|
|
104
|
users = query.all()
|
|
101
|
|
|
105
|
|
|
102
|
_users = [
|
|
106
|
_users = [
|
|
103
|
self._serialize_user(user) for user in users
|
|
107
|
self._serialize_user(user) for user in users
|
|
104
|
]
|
|
108
|
]
|
|
105
|
return _users
|
|
109
|
return _users
|
|
106
|
|
|
110
|
|
|
107
|
def get_by_username(self, username, cache=False, case_insensitive=False):
|
|
111
|
def get_by_username(self, username, cache=False, case_insensitive=False):
|
|
108
|
|
|
112
|
|
|
109
|
if case_insensitive:
|
|
113
|
if case_insensitive:
|
|
110
|
user = self.sa.query(User).filter(User.username.ilike(username))
|
|
114
|
user = self.sa.query(User).filter(User.username.ilike(username))
|
|
111
|
else:
|
|
115
|
else:
|
|
112
|
user = self.sa.query(User)\
|
|
116
|
user = self.sa.query(User)\
|
|
113
|
.filter(User.username == username)
|
|
117
|
.filter(User.username == username)
|
|
114
|
if cache:
|
|
118
|
if cache:
|
|
115
|
name_key = _hash_key(username)
|
|
119
|
name_key = _hash_key(username)
|
|
116
|
user = user.options(
|
|
120
|
user = user.options(
|
|
117
|
FromCache("sql_cache_short", "get_user_%s" % name_key))
|
|
121
|
FromCache("sql_cache_short", "get_user_%s" % name_key))
|
|
118
|
return user.scalar()
|
|
122
|
return user.scalar()
|
|
119
|
|
|
123
|
|
|
120
|
def get_by_email(self, email, cache=False, case_insensitive=False):
|
|
124
|
def get_by_email(self, email, cache=False, case_insensitive=False):
|
|
121
|
return User.get_by_email(email, case_insensitive, cache)
|
|
125
|
return User.get_by_email(email, case_insensitive, cache)
|
|
122
|
|
|
126
|
|
|
123
|
def get_by_auth_token(self, auth_token, cache=False):
|
|
127
|
def get_by_auth_token(self, auth_token, cache=False):
|
|
124
|
return User.get_by_auth_token(auth_token, cache)
|
|
128
|
return User.get_by_auth_token(auth_token, cache)
|
|
125
|
|
|
129
|
|
|
126
|
def get_active_user_count(self, cache=False):
|
|
130
|
def get_active_user_count(self, cache=False):
|
|
127
|
qry = User.query().filter(
|
|
131
|
qry = User.query().filter(
|
|
128
|
User.active == true()).filter(
|
|
132
|
User.active == true()).filter(
|
|
129
|
User.username != User.DEFAULT_USER)
|
|
133
|
User.username != User.DEFAULT_USER)
|
|
130
|
if cache:
|
|
134
|
if cache:
|
|
131
|
qry = qry.options(
|
|
135
|
qry = qry.options(
|
|
132
|
FromCache("sql_cache_short", "get_active_users"))
|
|
136
|
FromCache("sql_cache_short", "get_active_users"))
|
|
133
|
return qry.count()
|
|
137
|
return qry.count()
|
|
134
|
|
|
138
|
|
|
135
|
def create(self, form_data, cur_user=None):
|
|
139
|
def create(self, form_data, cur_user=None):
|
|
136
|
if not cur_user:
|
|
140
|
if not cur_user:
|
|
137
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
141
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
138
|
|
|
142
|
|
|
139
|
user_data = {
|
|
143
|
user_data = {
|
|
140
|
'username': form_data['username'],
|
|
144
|
'username': form_data['username'],
|
|
141
|
'password': form_data['password'],
|
|
145
|
'password': form_data['password'],
|
|
142
|
'email': form_data['email'],
|
|
146
|
'email': form_data['email'],
|
|
143
|
'firstname': form_data['firstname'],
|
|
147
|
'firstname': form_data['firstname'],
|
|
144
|
'lastname': form_data['lastname'],
|
|
148
|
'lastname': form_data['lastname'],
|
|
145
|
'active': form_data['active'],
|
|
149
|
'active': form_data['active'],
|
|
146
|
'extern_type': form_data['extern_type'],
|
|
150
|
'extern_type': form_data['extern_type'],
|
|
147
|
'extern_name': form_data['extern_name'],
|
|
151
|
'extern_name': form_data['extern_name'],
|
|
148
|
'admin': False,
|
|
152
|
'admin': False,
|
|
149
|
'cur_user': cur_user
|
|
153
|
'cur_user': cur_user
|
|
150
|
}
|
|
154
|
}
|
|
151
|
|
|
155
|
|
|
152
|
if 'create_repo_group' in form_data:
|
|
156
|
if 'create_repo_group' in form_data:
|
|
153
|
user_data['create_repo_group'] = str2bool(
|
|
157
|
user_data['create_repo_group'] = str2bool(
|
|
154
|
form_data.get('create_repo_group'))
|
|
158
|
form_data.get('create_repo_group'))
|
|
155
|
|
|
159
|
|
|
156
|
try:
|
|
160
|
try:
|
|
157
|
if form_data.get('password_change'):
|
|
161
|
if form_data.get('password_change'):
|
|
158
|
user_data['force_password_change'] = True
|
|
162
|
user_data['force_password_change'] = True
|
|
159
|
return UserModel().create_or_update(**user_data)
|
|
163
|
return UserModel().create_or_update(**user_data)
|
|
160
|
except Exception:
|
|
164
|
except Exception:
|
|
161
|
log.error(traceback.format_exc())
|
|
165
|
log.error(traceback.format_exc())
|
|
162
|
raise
|
|
166
|
raise
|
|
163
|
|
|
167
|
|
|
164
|
def update_user(self, user, skip_attrs=None, **kwargs):
|
|
168
|
def update_user(self, user, skip_attrs=None, **kwargs):
|
|
165
|
from rhodecode.lib.auth import get_crypt_password
|
|
169
|
from rhodecode.lib.auth import get_crypt_password
|
|
166
|
|
|
170
|
|
|
167
|
user = self._get_user(user)
|
|
171
|
user = self._get_user(user)
|
|
168
|
if user.username == User.DEFAULT_USER:
|
|
172
|
if user.username == User.DEFAULT_USER:
|
|
169
|
raise DefaultUserException(
|
|
173
|
raise DefaultUserException(
|
|
170
|
"You can't edit this user (`%(username)s`) since it's "
|
|
174
|
"You can't edit this user (`%(username)s`) since it's "
|
|
171
|
"crucial for entire application" % {
|
|
175
|
"crucial for entire application" % {
|
|
172
|
'username': user.username})
|
|
176
|
'username': user.username})
|
|
173
|
|
|
177
|
|
|
174
|
# first store only defaults
|
|
178
|
# first store only defaults
|
|
175
|
user_attrs = {
|
|
179
|
user_attrs = {
|
|
176
|
'updating_user_id': user.user_id,
|
|
180
|
'updating_user_id': user.user_id,
|
|
177
|
'username': user.username,
|
|
181
|
'username': user.username,
|
|
178
|
'password': user.password,
|
|
182
|
'password': user.password,
|
|
179
|
'email': user.email,
|
|
183
|
'email': user.email,
|
|
180
|
'firstname': user.name,
|
|
184
|
'firstname': user.name,
|
|
181
|
'lastname': user.lastname,
|
|
185
|
'lastname': user.lastname,
|
|
182
|
'description': user.description,
|
|
186
|
'description': user.description,
|
|
183
|
'active': user.active,
|
|
187
|
'active': user.active,
|
|
184
|
'admin': user.admin,
|
|
188
|
'admin': user.admin,
|
|
185
|
'extern_name': user.extern_name,
|
|
189
|
'extern_name': user.extern_name,
|
|
186
|
'extern_type': user.extern_type,
|
|
190
|
'extern_type': user.extern_type,
|
|
187
|
'language': user.user_data.get('language')
|
|
191
|
'language': user.user_data.get('language')
|
|
188
|
}
|
|
192
|
}
|
|
189
|
|
|
193
|
|
|
190
|
# in case there's new_password, that comes from form, use it to
|
|
194
|
# in case there's new_password, that comes from form, use it to
|
|
191
|
# store password
|
|
195
|
# store password
|
|
192
|
if kwargs.get('new_password'):
|
|
196
|
if kwargs.get('new_password'):
|
|
193
|
kwargs['password'] = kwargs['new_password']
|
|
197
|
kwargs['password'] = kwargs['new_password']
|
|
194
|
|
|
198
|
|
|
195
|
# cleanups, my_account password change form
|
|
199
|
# cleanups, my_account password change form
|
|
196
|
kwargs.pop('current_password', None)
|
|
200
|
kwargs.pop('current_password', None)
|
|
197
|
kwargs.pop('new_password', None)
|
|
201
|
kwargs.pop('new_password', None)
|
|
198
|
|
|
202
|
|
|
199
|
# cleanups, user edit password change form
|
|
203
|
# cleanups, user edit password change form
|
|
200
|
kwargs.pop('password_confirmation', None)
|
|
204
|
kwargs.pop('password_confirmation', None)
|
|
201
|
kwargs.pop('password_change', None)
|
|
205
|
kwargs.pop('password_change', None)
|
|
202
|
|
|
206
|
|
|
203
|
# create repo group on user creation
|
|
207
|
# create repo group on user creation
|
|
204
|
kwargs.pop('create_repo_group', None)
|
|
208
|
kwargs.pop('create_repo_group', None)
|
|
205
|
|
|
209
|
|
|
206
|
# legacy forms send name, which is the firstname
|
|
210
|
# legacy forms send name, which is the firstname
|
|
207
|
firstname = kwargs.pop('name', None)
|
|
211
|
firstname = kwargs.pop('name', None)
|
|
208
|
if firstname:
|
|
212
|
if firstname:
|
|
209
|
kwargs['firstname'] = firstname
|
|
213
|
kwargs['firstname'] = firstname
|
|
210
|
|
|
214
|
|
|
211
|
for k, v in kwargs.items():
|
|
215
|
for k, v in kwargs.items():
|
|
212
|
# skip if we don't want to update this
|
|
216
|
# skip if we don't want to update this
|
|
213
|
if skip_attrs and k in skip_attrs:
|
|
217
|
if skip_attrs and k in skip_attrs:
|
|
214
|
continue
|
|
218
|
continue
|
|
215
|
|
|
219
|
|
|
216
|
user_attrs[k] = v
|
|
220
|
user_attrs[k] = v
|
|
217
|
|
|
221
|
|
|
218
|
try:
|
|
222
|
try:
|
|
219
|
return self.create_or_update(**user_attrs)
|
|
223
|
return self.create_or_update(**user_attrs)
|
|
220
|
except Exception:
|
|
224
|
except Exception:
|
|
221
|
log.error(traceback.format_exc())
|
|
225
|
log.error(traceback.format_exc())
|
|
222
|
raise
|
|
226
|
raise
|
|
223
|
|
|
227
|
|
|
224
|
def create_or_update(
|
|
228
|
def create_or_update(
|
|
225
|
self, username, password, email, firstname='', lastname='',
|
|
229
|
self, username, password, email, firstname='', lastname='',
|
|
226
|
active=True, admin=False, extern_type=None, extern_name=None,
|
|
230
|
active=True, admin=False, extern_type=None, extern_name=None,
|
|
227
|
cur_user=None, plugin=None, force_password_change=False,
|
|
231
|
cur_user=None, plugin=None, force_password_change=False,
|
|
228
|
allow_to_create_user=True, create_repo_group=None,
|
|
232
|
allow_to_create_user=True, create_repo_group=None,
|
|
229
|
updating_user_id=None, language=None, description='',
|
|
233
|
updating_user_id=None, language=None, description='',
|
|
230
|
strict_creation_check=True):
|
|
234
|
strict_creation_check=True):
|
|
231
|
"""
|
|
235
|
"""
|
|
232
|
Creates a new instance if not found, or updates current one
|
|
236
|
Creates a new instance if not found, or updates current one
|
|
233
|
|
|
237
|
|
|
234
|
:param username:
|
|
238
|
:param username:
|
|
235
|
:param password:
|
|
239
|
:param password:
|
|
236
|
:param email:
|
|
240
|
:param email:
|
|
237
|
:param firstname:
|
|
241
|
:param firstname:
|
|
238
|
:param lastname:
|
|
242
|
:param lastname:
|
|
239
|
:param active:
|
|
243
|
:param active:
|
|
240
|
:param admin:
|
|
244
|
:param admin:
|
|
241
|
:param extern_type:
|
|
245
|
:param extern_type:
|
|
242
|
:param extern_name:
|
|
246
|
:param extern_name:
|
|
243
|
:param cur_user:
|
|
247
|
:param cur_user:
|
|
244
|
:param plugin: optional plugin this method was called from
|
|
248
|
:param plugin: optional plugin this method was called from
|
|
245
|
:param force_password_change: toggles new or existing user flag
|
|
249
|
:param force_password_change: toggles new or existing user flag
|
|
246
|
for password change
|
|
250
|
for password change
|
|
247
|
:param allow_to_create_user: Defines if the method can actually create
|
|
251
|
:param allow_to_create_user: Defines if the method can actually create
|
|
248
|
new users
|
|
252
|
new users
|
|
249
|
:param create_repo_group: Defines if the method should also
|
|
253
|
:param create_repo_group: Defines if the method should also
|
|
250
|
create an repo group with user name, and owner
|
|
254
|
create an repo group with user name, and owner
|
|
251
|
:param updating_user_id: if we set it up this is the user we want to
|
|
255
|
:param updating_user_id: if we set it up this is the user we want to
|
|
252
|
update this allows to editing username.
|
|
256
|
update this allows to editing username.
|
|
253
|
:param language: language of user from interface.
|
|
257
|
:param language: language of user from interface.
|
|
254
|
:param description: user description
|
|
258
|
:param description: user description
|
|
255
|
:param strict_creation_check: checks for allowed creation license wise etc.
|
|
259
|
:param strict_creation_check: checks for allowed creation license wise etc.
|
|
256
|
|
|
260
|
|
|
257
|
:returns: new User object with injected `is_new_user` attribute.
|
|
261
|
:returns: new User object with injected `is_new_user` attribute.
|
|
258
|
"""
|
|
262
|
"""
|
|
259
|
|
|
263
|
|
|
260
|
if not cur_user:
|
|
264
|
if not cur_user:
|
|
261
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
265
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
262
|
|
|
266
|
|
|
263
|
from rhodecode.lib.auth import (
|
|
267
|
from rhodecode.lib.auth import (
|
|
264
|
get_crypt_password, check_password)
|
|
268
|
get_crypt_password, check_password)
|
|
265
|
from rhodecode.lib import hooks_base
|
|
269
|
from rhodecode.lib import hooks_base
|
|
266
|
|
|
270
|
|
|
267
|
def _password_change(new_user, password):
|
|
271
|
def _password_change(new_user, password):
|
|
268
|
old_password = new_user.password or ''
|
|
272
|
old_password = new_user.password or ''
|
|
269
|
# empty password
|
|
273
|
# empty password
|
|
270
|
if not old_password:
|
|
274
|
if not old_password:
|
|
271
|
return False
|
|
275
|
return False
|
|
272
|
|
|
276
|
|
|
273
|
# password check is only needed for RhodeCode internal auth calls
|
|
277
|
# password check is only needed for RhodeCode internal auth calls
|
|
274
|
# in case it's a plugin we don't care
|
|
278
|
# in case it's a plugin we don't care
|
|
275
|
if not plugin:
|
|
279
|
if not plugin:
|
|
276
|
|
|
280
|
|
|
277
|
# first check if we gave crypted password back, and if it
|
|
281
|
# first check if we gave crypted password back, and if it
|
|
278
|
# matches it's not password change
|
|
282
|
# matches it's not password change
|
|
279
|
if new_user.password == password:
|
|
283
|
if new_user.password == password:
|
|
280
|
return False
|
|
284
|
return False
|
|
281
|
|
|
285
|
|
|
282
|
password_match = check_password(password, old_password)
|
|
286
|
password_match = check_password(password, old_password)
|
|
283
|
if not password_match:
|
|
287
|
if not password_match:
|
|
284
|
return True
|
|
288
|
return True
|
|
285
|
|
|
289
|
|
|
286
|
return False
|
|
290
|
return False
|
|
287
|
|
|
291
|
|
|
288
|
# read settings on default personal repo group creation
|
|
292
|
# read settings on default personal repo group creation
|
|
289
|
if create_repo_group is None:
|
|
293
|
if create_repo_group is None:
|
|
290
|
default_create_repo_group = RepoGroupModel()\
|
|
294
|
default_create_repo_group = RepoGroupModel()\
|
|
291
|
.get_default_create_personal_repo_group()
|
|
295
|
.get_default_create_personal_repo_group()
|
|
292
|
create_repo_group = default_create_repo_group
|
|
296
|
create_repo_group = default_create_repo_group
|
|
293
|
|
|
297
|
|
|
294
|
user_data = {
|
|
298
|
user_data = {
|
|
295
|
'username': username,
|
|
299
|
'username': username,
|
|
296
|
'password': password,
|
|
300
|
'password': password,
|
|
297
|
'email': email,
|
|
301
|
'email': email,
|
|
298
|
'firstname': firstname,
|
|
302
|
'firstname': firstname,
|
|
299
|
'lastname': lastname,
|
|
303
|
'lastname': lastname,
|
|
300
|
'active': active,
|
|
304
|
'active': active,
|
|
301
|
'admin': admin
|
|
305
|
'admin': admin
|
|
302
|
}
|
|
306
|
}
|
|
303
|
|
|
307
|
|
|
304
|
if updating_user_id:
|
|
308
|
if updating_user_id:
|
|
305
|
log.debug('Checking for existing account in RhodeCode '
|
|
309
|
log.debug('Checking for existing account in RhodeCode '
|
|
306
|
'database with user_id `%s` ', updating_user_id)
|
|
310
|
'database with user_id `%s` ', updating_user_id)
|
|
307
|
user = User.get(updating_user_id)
|
|
311
|
user = User.get(updating_user_id)
|
|
308
|
else:
|
|
312
|
else:
|
|
309
|
log.debug('Checking for existing account in RhodeCode '
|
|
313
|
log.debug('Checking for existing account in RhodeCode '
|
|
310
|
'database with username `%s` ', username)
|
|
314
|
'database with username `%s` ', username)
|
|
311
|
user = User.get_by_username(username, case_insensitive=True)
|
|
315
|
user = User.get_by_username(username, case_insensitive=True)
|
|
312
|
|
|
316
|
|
|
313
|
if user is None:
|
|
317
|
if user is None:
|
|
314
|
# we check internal flag if this method is actually allowed to
|
|
318
|
# we check internal flag if this method is actually allowed to
|
|
315
|
# create new user
|
|
319
|
# create new user
|
|
316
|
if not allow_to_create_user:
|
|
320
|
if not allow_to_create_user:
|
|
317
|
msg = ('Method wants to create new user, but it is not '
|
|
321
|
msg = ('Method wants to create new user, but it is not '
|
|
318
|
'allowed to do so')
|
|
322
|
'allowed to do so')
|
|
319
|
log.warning(msg)
|
|
323
|
log.warning(msg)
|
|
320
|
raise NotAllowedToCreateUserError(msg)
|
|
324
|
raise NotAllowedToCreateUserError(msg)
|
|
321
|
|
|
325
|
|
|
322
|
log.debug('Creating new user %s', username)
|
|
326
|
log.debug('Creating new user %s', username)
|
|
323
|
|
|
327
|
|
|
324
|
# only if we create user that is active
|
|
328
|
# only if we create user that is active
|
|
325
|
new_active_user = active
|
|
329
|
new_active_user = active
|
|
326
|
if new_active_user and strict_creation_check:
|
|
330
|
if new_active_user and strict_creation_check:
|
|
327
|
# raises UserCreationError if it's not allowed for any reason to
|
|
331
|
# raises UserCreationError if it's not allowed for any reason to
|
|
328
|
# create new active user, this also executes pre-create hooks
|
|
332
|
# create new active user, this also executes pre-create hooks
|
|
329
|
hooks_base.check_allowed_create_user(user_data, cur_user, strict_check=True)
|
|
333
|
hooks_base.check_allowed_create_user(user_data, cur_user, strict_check=True)
|
|
330
|
events.trigger(events.UserPreCreate(user_data))
|
|
334
|
events.trigger(events.UserPreCreate(user_data))
|
|
331
|
new_user = User()
|
|
335
|
new_user = User()
|
|
332
|
edit = False
|
|
336
|
edit = False
|
|
333
|
else:
|
|
337
|
else:
|
|
334
|
log.debug('updating user `%s`', username)
|
|
338
|
log.debug('updating user `%s`', username)
|
|
335
|
events.trigger(events.UserPreUpdate(user, user_data))
|
|
339
|
events.trigger(events.UserPreUpdate(user, user_data))
|
|
336
|
new_user = user
|
|
340
|
new_user = user
|
|
337
|
edit = True
|
|
341
|
edit = True
|
|
338
|
|
|
342
|
|
|
339
|
# we're not allowed to edit default user
|
|
343
|
# we're not allowed to edit default user
|
|
340
|
if user.username == User.DEFAULT_USER:
|
|
344
|
if user.username == User.DEFAULT_USER:
|
|
341
|
raise DefaultUserException(
|
|
345
|
raise DefaultUserException(
|
|
342
|
"You can't edit this user (`%(username)s`) since it's "
|
|
346
|
"You can't edit this user (`%(username)s`) since it's "
|
|
343
|
"crucial for entire application"
|
|
347
|
"crucial for entire application"
|
|
344
|
% {'username': user.username})
|
|
348
|
% {'username': user.username})
|
|
345
|
|
|
349
|
|
|
346
|
# inject special attribute that will tell us if User is new or old
|
|
350
|
# inject special attribute that will tell us if User is new or old
|
|
347
|
new_user.is_new_user = not edit
|
|
351
|
new_user.is_new_user = not edit
|
|
348
|
# for users that didn's specify auth type, we use RhodeCode built in
|
|
352
|
# for users that didn's specify auth type, we use RhodeCode built in
|
|
349
|
from rhodecode.authentication.plugins import auth_rhodecode
|
|
353
|
from rhodecode.authentication.plugins import auth_rhodecode
|
|
350
|
extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.uid
|
|
354
|
extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.uid
|
|
351
|
extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.uid
|
|
355
|
extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.uid
|
|
352
|
|
|
356
|
|
|
353
|
try:
|
|
357
|
try:
|
|
354
|
new_user.username = username
|
|
358
|
new_user.username = username
|
|
355
|
new_user.admin = admin
|
|
359
|
new_user.admin = admin
|
|
356
|
new_user.email = email
|
|
360
|
new_user.email = email
|
|
357
|
new_user.active = active
|
|
361
|
new_user.active = active
|
|
358
|
new_user.extern_name = safe_unicode(extern_name)
|
|
362
|
new_user.extern_name = safe_unicode(extern_name)
|
|
359
|
new_user.extern_type = safe_unicode(extern_type)
|
|
363
|
new_user.extern_type = safe_unicode(extern_type)
|
|
360
|
new_user.name = firstname
|
|
364
|
new_user.name = firstname
|
|
361
|
new_user.lastname = lastname
|
|
365
|
new_user.lastname = lastname
|
|
362
|
new_user.description = description
|
|
366
|
new_user.description = description
|
|
363
|
|
|
367
|
|
|
364
|
# set password only if creating an user or password is changed
|
|
368
|
# set password only if creating an user or password is changed
|
|
365
|
if not edit or _password_change(new_user, password):
|
|
369
|
if not edit or _password_change(new_user, password):
|
|
366
|
reason = 'new password' if edit else 'new user'
|
|
370
|
reason = 'new password' if edit else 'new user'
|
|
367
|
log.debug('Updating password reason=>%s', reason)
|
|
371
|
log.debug('Updating password reason=>%s', reason)
|
|
368
|
new_user.password = get_crypt_password(password) if password else None
|
|
372
|
new_user.password = get_crypt_password(password) if password else None
|
|
369
|
|
|
373
|
|
|
370
|
if force_password_change:
|
|
374
|
if force_password_change:
|
|
371
|
new_user.update_userdata(force_password_change=True)
|
|
375
|
new_user.update_userdata(force_password_change=True)
|
|
372
|
if language:
|
|
376
|
if language:
|
|
373
|
new_user.update_userdata(language=language)
|
|
377
|
new_user.update_userdata(language=language)
|
|
374
|
new_user.update_userdata(notification_status=True)
|
|
378
|
new_user.update_userdata(notification_status=True)
|
|
375
|
|
|
379
|
|
|
376
|
self.sa.add(new_user)
|
|
380
|
self.sa.add(new_user)
|
|
377
|
|
|
381
|
|
|
378
|
if not edit and create_repo_group:
|
|
382
|
if not edit and create_repo_group:
|
|
379
|
RepoGroupModel().create_personal_repo_group(
|
|
383
|
RepoGroupModel().create_personal_repo_group(
|
|
380
|
new_user, commit_early=False)
|
|
384
|
new_user, commit_early=False)
|
|
381
|
|
|
385
|
|
|
382
|
if not edit:
|
|
386
|
if not edit:
|
|
383
|
# add the RSS token
|
|
387
|
# add the RSS token
|
|
384
|
self.add_auth_token(
|
|
388
|
self.add_auth_token(
|
|
385
|
user=username, lifetime_minutes=-1,
|
|
389
|
user=username, lifetime_minutes=-1,
|
|
386
|
role=self.auth_token_role.ROLE_FEED,
|
|
390
|
role=self.auth_token_role.ROLE_FEED,
|
|
387
|
description=u'Generated feed token')
|
|
391
|
description=u'Generated feed token')
|
|
388
|
|
|
392
|
|
|
389
|
kwargs = new_user.get_dict()
|
|
393
|
kwargs = new_user.get_dict()
|
|
390
|
# backward compat, require api_keys present
|
|
394
|
# backward compat, require api_keys present
|
|
391
|
kwargs['api_keys'] = kwargs['auth_tokens']
|
|
395
|
kwargs['api_keys'] = kwargs['auth_tokens']
|
|
392
|
hooks_base.create_user(created_by=cur_user, **kwargs)
|
|
396
|
hooks_base.create_user(created_by=cur_user, **kwargs)
|
|
393
|
events.trigger(events.UserPostCreate(user_data))
|
|
397
|
events.trigger(events.UserPostCreate(user_data))
|
|
394
|
return new_user
|
|
398
|
return new_user
|
|
395
|
except (DatabaseError,):
|
|
399
|
except (DatabaseError,):
|
|
396
|
log.error(traceback.format_exc())
|
|
400
|
log.error(traceback.format_exc())
|
|
397
|
raise
|
|
401
|
raise
|
|
398
|
|
|
402
|
|
|
399
|
def create_registration(self, form_data,
|
|
403
|
def create_registration(self, form_data,
|
|
400
|
extern_name='rhodecode', extern_type='rhodecode'):
|
|
404
|
extern_name='rhodecode', extern_type='rhodecode'):
|
|
401
|
from rhodecode.model.notification import NotificationModel
|
|
405
|
from rhodecode.model.notification import NotificationModel
|
|
402
|
from rhodecode.model.notification import EmailNotificationModel
|
|
406
|
from rhodecode.model.notification import EmailNotificationModel
|
|
403
|
|
|
407
|
|
|
404
|
try:
|
|
408
|
try:
|
|
405
|
form_data['admin'] = False
|
|
409
|
form_data['admin'] = False
|
|
406
|
form_data['extern_name'] = extern_name
|
|
410
|
form_data['extern_name'] = extern_name
|
|
407
|
form_data['extern_type'] = extern_type
|
|
411
|
form_data['extern_type'] = extern_type
|
|
408
|
new_user = self.create(form_data)
|
|
412
|
new_user = self.create(form_data)
|
|
409
|
|
|
413
|
|
|
410
|
self.sa.add(new_user)
|
|
414
|
self.sa.add(new_user)
|
|
411
|
self.sa.flush()
|
|
415
|
self.sa.flush()
|
|
412
|
|
|
416
|
|
|
413
|
user_data = new_user.get_dict()
|
|
417
|
user_data = new_user.get_dict()
|
|
414
|
user_data.update({
|
|
418
|
user_data.update({
|
|
415
|
'first_name': user_data.get('firstname'),
|
|
419
|
'first_name': user_data.get('firstname'),
|
|
416
|
'last_name': user_data.get('lastname'),
|
|
420
|
'last_name': user_data.get('lastname'),
|
|
417
|
})
|
|
421
|
})
|
|
418
|
kwargs = {
|
|
422
|
kwargs = {
|
|
419
|
# use SQLALCHEMY safe dump of user data
|
|
423
|
# use SQLALCHEMY safe dump of user data
|
|
420
|
'user': AttributeDict(user_data),
|
|
424
|
'user': AttributeDict(user_data),
|
|
421
|
'date': datetime.datetime.now()
|
|
425
|
'date': datetime.datetime.now()
|
|
422
|
}
|
|
426
|
}
|
|
423
|
notification_type = EmailNotificationModel.TYPE_REGISTRATION
|
|
427
|
notification_type = EmailNotificationModel.TYPE_REGISTRATION
|
|
424
|
# pre-generate the subject for notification itself
|
|
428
|
# pre-generate the subject for notification itself
|
|
425
|
(subject, _e, body_plaintext) = EmailNotificationModel().render_email(
|
|
429
|
(subject, _e, body_plaintext) = EmailNotificationModel().render_email(
|
|
426
|
notification_type, **kwargs)
|
|
430
|
notification_type, **kwargs)
|
|
427
|
|
|
431
|
|
|
428
|
# create notification objects, and emails
|
|
432
|
# create notification objects, and emails
|
|
429
|
NotificationModel().create(
|
|
433
|
NotificationModel().create(
|
|
430
|
created_by=new_user,
|
|
434
|
created_by=new_user,
|
|
431
|
notification_subject=subject,
|
|
435
|
notification_subject=subject,
|
|
432
|
notification_body=body_plaintext,
|
|
436
|
notification_body=body_plaintext,
|
|
433
|
notification_type=notification_type,
|
|
437
|
notification_type=notification_type,
|
|
434
|
recipients=None, # all admins
|
|
438
|
recipients=None, # all admins
|
|
435
|
email_kwargs=kwargs,
|
|
439
|
email_kwargs=kwargs,
|
|
436
|
)
|
|
440
|
)
|
|
437
|
|
|
441
|
|
|
438
|
return new_user
|
|
442
|
return new_user
|
|
439
|
except Exception:
|
|
443
|
except Exception:
|
|
440
|
log.error(traceback.format_exc())
|
|
444
|
log.error(traceback.format_exc())
|
|
441
|
raise
|
|
445
|
raise
|
|
442
|
|
|
446
|
|
|
443
|
def _handle_user_repos(self, username, repositories, handle_user,
|
|
447
|
def _handle_user_repos(self, username, repositories, handle_user,
|
|
444
|
handle_mode=None):
|
|
448
|
handle_mode=None):
|
|
445
|
|
|
449
|
|
|
446
|
left_overs = True
|
|
450
|
left_overs = True
|
|
447
|
|
|
451
|
|
|
448
|
from rhodecode.model.repo import RepoModel
|
|
452
|
from rhodecode.model.repo import RepoModel
|
|
449
|
|
|
453
|
|
|
450
|
if handle_mode == 'detach':
|
|
454
|
if handle_mode == 'detach':
|
|
451
|
for obj in repositories:
|
|
455
|
for obj in repositories:
|
|
452
|
obj.user = handle_user
|
|
456
|
obj.user = handle_user
|
|
453
|
# set description we know why we super admin now owns
|
|
457
|
# set description we know why we super admin now owns
|
|
454
|
# additional repositories that were orphaned !
|
|
458
|
# additional repositories that were orphaned !
|
|
455
|
obj.description += ' \n::detached repository from deleted user: %s' % (username,)
|
|
459
|
obj.description += ' \n::detached repository from deleted user: %s' % (username,)
|
|
456
|
self.sa.add(obj)
|
|
460
|
self.sa.add(obj)
|
|
457
|
left_overs = False
|
|
461
|
left_overs = False
|
|
458
|
elif handle_mode == 'delete':
|
|
462
|
elif handle_mode == 'delete':
|
|
459
|
for obj in repositories:
|
|
463
|
for obj in repositories:
|
|
460
|
RepoModel().delete(obj, forks='detach')
|
|
464
|
RepoModel().delete(obj, forks='detach')
|
|
461
|
left_overs = False
|
|
465
|
left_overs = False
|
|
462
|
|
|
466
|
|
|
463
|
# if nothing is done we have left overs left
|
|
467
|
# if nothing is done we have left overs left
|
|
464
|
return left_overs
|
|
468
|
return left_overs
|
|
465
|
|
|
469
|
|
|
466
|
def _handle_user_repo_groups(self, username, repository_groups, handle_user,
|
|
470
|
def _handle_user_repo_groups(self, username, repository_groups, handle_user,
|
|
467
|
handle_mode=None):
|
|
471
|
handle_mode=None):
|
|
468
|
|
|
472
|
|
|
469
|
left_overs = True
|
|
473
|
left_overs = True
|
|
470
|
|
|
474
|
|
|
471
|
from rhodecode.model.repo_group import RepoGroupModel
|
|
475
|
from rhodecode.model.repo_group import RepoGroupModel
|
|
472
|
|
|
476
|
|
|
473
|
if handle_mode == 'detach':
|
|
477
|
if handle_mode == 'detach':
|
|
474
|
for r in repository_groups:
|
|
478
|
for r in repository_groups:
|
|
475
|
r.user = handle_user
|
|
479
|
r.user = handle_user
|
|
476
|
# set description we know why we super admin now owns
|
|
480
|
# set description we know why we super admin now owns
|
|
477
|
# additional repositories that were orphaned !
|
|
481
|
# additional repositories that were orphaned !
|
|
478
|
r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
|
|
482
|
r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
|
|
479
|
r.personal = False
|
|
483
|
r.personal = False
|
|
480
|
self.sa.add(r)
|
|
484
|
self.sa.add(r)
|
|
481
|
left_overs = False
|
|
485
|
left_overs = False
|
|
482
|
elif handle_mode == 'delete':
|
|
486
|
elif handle_mode == 'delete':
|
|
483
|
for r in repository_groups:
|
|
487
|
for r in repository_groups:
|
|
484
|
RepoGroupModel().delete(r)
|
|
488
|
RepoGroupModel().delete(r)
|
|
485
|
left_overs = False
|
|
489
|
left_overs = False
|
|
486
|
|
|
490
|
|
|
487
|
# if nothing is done we have left overs left
|
|
491
|
# if nothing is done we have left overs left
|
|
488
|
return left_overs
|
|
492
|
return left_overs
|
|
489
|
|
|
493
|
|
|
490
|
def _handle_user_user_groups(self, username, user_groups, handle_user,
|
|
494
|
def _handle_user_user_groups(self, username, user_groups, handle_user,
|
|
491
|
handle_mode=None):
|
|
495
|
handle_mode=None):
|
|
492
|
|
|
496
|
|
|
493
|
left_overs = True
|
|
497
|
left_overs = True
|
|
494
|
|
|
498
|
|
|
495
|
from rhodecode.model.user_group import UserGroupModel
|
|
499
|
from rhodecode.model.user_group import UserGroupModel
|
|
496
|
|
|
500
|
|
|
497
|
if handle_mode == 'detach':
|
|
501
|
if handle_mode == 'detach':
|
|
498
|
for r in user_groups:
|
|
502
|
for r in user_groups:
|
|
499
|
for user_user_group_to_perm in r.user_user_group_to_perm:
|
|
503
|
for user_user_group_to_perm in r.user_user_group_to_perm:
|
|
500
|
if user_user_group_to_perm.user.username == username:
|
|
504
|
if user_user_group_to_perm.user.username == username:
|
|
501
|
user_user_group_to_perm.user = handle_user
|
|
505
|
user_user_group_to_perm.user = handle_user
|
|
502
|
r.user = handle_user
|
|
506
|
r.user = handle_user
|
|
503
|
# set description we know why we super admin now owns
|
|
507
|
# set description we know why we super admin now owns
|
|
504
|
# additional repositories that were orphaned !
|
|
508
|
# additional repositories that were orphaned !
|
|
505
|
r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
|
|
509
|
r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
|
|
506
|
self.sa.add(r)
|
|
510
|
self.sa.add(r)
|
|
507
|
left_overs = False
|
|
511
|
left_overs = False
|
|
508
|
elif handle_mode == 'delete':
|
|
512
|
elif handle_mode == 'delete':
|
|
509
|
for r in user_groups:
|
|
513
|
for r in user_groups:
|
|
510
|
UserGroupModel().delete(r)
|
|
514
|
UserGroupModel().delete(r)
|
|
511
|
left_overs = False
|
|
515
|
left_overs = False
|
|
512
|
|
|
516
|
|
|
513
|
# if nothing is done we have left overs left
|
|
517
|
# if nothing is done we have left overs left
|
|
514
|
return left_overs
|
|
518
|
return left_overs
|
|
515
|
|
|
519
|
|
|
516
|
def _handle_user_pull_requests(self, username, pull_requests, handle_user,
|
|
520
|
def _handle_user_pull_requests(self, username, pull_requests, handle_user,
|
|
517
|
handle_mode=None):
|
|
521
|
handle_mode=None):
|
|
518
|
left_overs = True
|
|
522
|
left_overs = True
|
|
519
|
|
|
523
|
|
|
520
|
from rhodecode.model.pull_request import PullRequestModel
|
|
524
|
from rhodecode.model.pull_request import PullRequestModel
|
|
521
|
|
|
525
|
|
|
522
|
if handle_mode == 'detach':
|
|
526
|
if handle_mode == 'detach':
|
|
523
|
for pr in pull_requests:
|
|
527
|
for pr in pull_requests:
|
|
524
|
pr.user_id = handle_user.user_id
|
|
528
|
pr.user_id = handle_user.user_id
|
|
525
|
# set description we know why we super admin now owns
|
|
529
|
# set description we know why we super admin now owns
|
|
526
|
# additional repositories that were orphaned !
|
|
530
|
# additional repositories that were orphaned !
|
|
527
|
pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
|
|
531
|
pr.description += ' \n::detached pull requests from deleted user: %s' % (username,)
|
|
528
|
self.sa.add(pr)
|
|
532
|
self.sa.add(pr)
|
|
529
|
left_overs = False
|
|
533
|
left_overs = False
|
|
530
|
elif handle_mode == 'delete':
|
|
534
|
elif handle_mode == 'delete':
|
|
531
|
for pr in pull_requests:
|
|
535
|
for pr in pull_requests:
|
|
532
|
PullRequestModel().delete(pr)
|
|
536
|
PullRequestModel().delete(pr)
|
|
533
|
|
|
537
|
|
|
534
|
left_overs = False
|
|
538
|
left_overs = False
|
|
535
|
|
|
539
|
|
|
536
|
# if nothing is done we have left overs left
|
|
540
|
# if nothing is done we have left overs left
|
|
537
|
return left_overs
|
|
541
|
return left_overs
|
|
538
|
|
|
542
|
|
|
539
|
def _handle_user_artifacts(self, username, artifacts, handle_user,
|
|
543
|
def _handle_user_artifacts(self, username, artifacts, handle_user,
|
|
540
|
handle_mode=None):
|
|
544
|
handle_mode=None):
|
|
541
|
|
|
545
|
|
|
542
|
left_overs = True
|
|
546
|
left_overs = True
|
|
543
|
|
|
547
|
|
|
544
|
if handle_mode == 'detach':
|
|
548
|
if handle_mode == 'detach':
|
|
545
|
for a in artifacts:
|
|
549
|
for a in artifacts:
|
|
546
|
a.upload_user = handle_user
|
|
550
|
a.upload_user = handle_user
|
|
547
|
# set description we know why we super admin now owns
|
|
551
|
# set description we know why we super admin now owns
|
|
548
|
# additional artifacts that were orphaned !
|
|
552
|
# additional artifacts that were orphaned !
|
|
549
|
a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
|
|
553
|
a.file_description += ' \n::detached artifact from deleted user: %s' % (username,)
|
|
550
|
self.sa.add(a)
|
|
554
|
self.sa.add(a)
|
|
551
|
left_overs = False
|
|
555
|
left_overs = False
|
|
552
|
elif handle_mode == 'delete':
|
|
556
|
elif handle_mode == 'delete':
|
|
553
|
from rhodecode.apps.file_store import utils as store_utils
|
|
557
|
from rhodecode.apps.file_store import utils as store_utils
|
|
554
|
request = get_current_request()
|
|
558
|
request = get_current_request()
|
|
555
|
storage = store_utils.get_file_storage(request.registry.settings)
|
|
559
|
storage = store_utils.get_file_storage(request.registry.settings)
|
|
556
|
for a in artifacts:
|
|
560
|
for a in artifacts:
|
|
557
|
file_uid = a.file_uid
|
|
561
|
file_uid = a.file_uid
|
|
558
|
storage.delete(file_uid)
|
|
562
|
storage.delete(file_uid)
|
|
559
|
self.sa.delete(a)
|
|
563
|
self.sa.delete(a)
|
|
560
|
|
|
564
|
|
|
561
|
left_overs = False
|
|
565
|
left_overs = False
|
|
562
|
|
|
566
|
|
|
563
|
# if nothing is done we have left overs left
|
|
567
|
# if nothing is done we have left overs left
|
|
564
|
return left_overs
|
|
568
|
return left_overs
|
|
565
|
|
|
569
|
|
|
566
|
def delete(self, user, cur_user=None, handle_repos=None,
|
|
570
|
def delete(self, user, cur_user=None, handle_repos=None,
|
|
567
|
handle_repo_groups=None, handle_user_groups=None,
|
|
571
|
handle_repo_groups=None, handle_user_groups=None,
|
|
568
|
handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
|
|
572
|
handle_pull_requests=None, handle_artifacts=None, handle_new_owner=None):
|
|
569
|
from rhodecode.lib import hooks_base
|
|
573
|
from rhodecode.lib import hooks_base
|
|
570
|
|
|
574
|
|
|
571
|
if not cur_user:
|
|
575
|
if not cur_user:
|
|
572
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
576
|
cur_user = getattr(get_current_rhodecode_user(), 'username', None)
|
|
573
|
|
|
577
|
|
|
574
|
user = self._get_user(user)
|
|
578
|
user = self._get_user(user)
|
|
575
|
|
|
579
|
|
|
576
|
try:
|
|
580
|
try:
|
|
577
|
if user.username == User.DEFAULT_USER:
|
|
581
|
if user.username == User.DEFAULT_USER:
|
|
578
|
raise DefaultUserException(
|
|
582
|
raise DefaultUserException(
|
|
579
|
u"You can't remove this user since it's"
|
|
583
|
u"You can't remove this user since it's"
|
|
580
|
u" crucial for entire application")
|
|
584
|
u" crucial for entire application")
|
|
581
|
handle_user = handle_new_owner or self.cls.get_first_super_admin()
|
|
585
|
handle_user = handle_new_owner or self.cls.get_first_super_admin()
|
|
582
|
log.debug('New detached objects owner %s', handle_user)
|
|
586
|
log.debug('New detached objects owner %s', handle_user)
|
|
583
|
|
|
587
|
|
|
584
|
left_overs = self._handle_user_repos(
|
|
588
|
left_overs = self._handle_user_repos(
|
|
585
|
user.username, user.repositories, handle_user, handle_repos)
|
|
589
|
user.username, user.repositories, handle_user, handle_repos)
|
|
586
|
if left_overs and user.repositories:
|
|
590
|
if left_overs and user.repositories:
|
|
587
|
repos = [x.repo_name for x in user.repositories]
|
|
591
|
repos = [x.repo_name for x in user.repositories]
|
|
588
|
raise UserOwnsReposException(
|
|
592
|
raise UserOwnsReposException(
|
|
589
|
u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
|
|
593
|
u'user "%(username)s" still owns %(len_repos)s repositories and cannot be '
|
|
590
|
u'removed. Switch owners or remove those repositories:%(list_repos)s'
|
|
594
|
u'removed. Switch owners or remove those repositories:%(list_repos)s'
|
|
591
|
% {'username': user.username, 'len_repos': len(repos),
|
|
595
|
% {'username': user.username, 'len_repos': len(repos),
|
|
592
|
'list_repos': ', '.join(repos)})
|
|
596
|
'list_repos': ', '.join(repos)})
|
|
593
|
|
|