##// END OF EJS Templates
api: added last-activity into returned data of get_user api....
marcink -
r1558:107da576 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,472 +1,473 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-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 import logging
21 import logging
22
22
23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
24 from rhodecode.api.utils import (
24 from rhodecode.api.utils import (
25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
27 from rhodecode.lib.exceptions import DefaultUserException
27 from rhodecode.lib.exceptions import DefaultUserException
28 from rhodecode.lib.utils2 import safe_int, str2bool
28 from rhodecode.lib.utils2 import safe_int, str2bool
29 from rhodecode.model.db import Session, User, Repository
29 from rhodecode.model.db import Session, User, Repository
30 from rhodecode.model.user import UserModel
30 from rhodecode.model.user import UserModel
31
31
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 @jsonrpc_method()
36 @jsonrpc_method()
37 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
37 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
38 """
38 """
39 Returns the information associated with a username or userid.
39 Returns the information associated with a username or userid.
40
40
41 * If the ``userid`` is not set, this command returns the information
41 * If the ``userid`` is not set, this command returns the information
42 for the ``userid`` calling the method.
42 for the ``userid`` calling the method.
43
43
44 .. note::
44 .. note::
45
45
46 Normal users may only run this command against their ``userid``. For
46 Normal users may only run this command against their ``userid``. For
47 full privileges you must run this command using an |authtoken| with
47 full privileges you must run this command using an |authtoken| with
48 admin rights.
48 admin rights.
49
49
50 :param apiuser: This is filled automatically from the |authtoken|.
50 :param apiuser: This is filled automatically from the |authtoken|.
51 :type apiuser: AuthUser
51 :type apiuser: AuthUser
52 :param userid: Sets the userid for which data will be returned.
52 :param userid: Sets the userid for which data will be returned.
53 :type userid: Optional(str or int)
53 :type userid: Optional(str or int)
54
54
55 Example output:
55 Example output:
56
56
57 .. code-block:: bash
57 .. code-block:: bash
58
58
59 {
59 {
60 "error": null,
60 "error": null,
61 "id": <id>,
61 "id": <id>,
62 "result": {
62 "result": {
63 "active": true,
63 "active": true,
64 "admin": false,
64 "admin": false,
65 "api_keys": [ list of keys ],
65 "api_keys": [ list of keys ],
66 "auth_tokens": [ list of tokens with details ],
66 "auth_tokens": [ list of tokens with details ],
67 "email": "user@example.com",
67 "email": "user@example.com",
68 "emails": [
68 "emails": [
69 "user@example.com"
69 "user@example.com"
70 ],
70 ],
71 "extern_name": "rhodecode",
71 "extern_name": "rhodecode",
72 "extern_type": "rhodecode",
72 "extern_type": "rhodecode",
73 "firstname": "username",
73 "firstname": "username",
74 "ip_addresses": [],
74 "ip_addresses": [],
75 "language": null,
75 "language": null,
76 "last_login": "Timestamp",
76 "last_login": "Timestamp",
77 "last_activity": "Timestamp",
77 "lastname": "surnae",
78 "lastname": "surnae",
78 "permissions": {
79 "permissions": {
79 "global": [
80 "global": [
80 "hg.inherit_default_perms.true",
81 "hg.inherit_default_perms.true",
81 "usergroup.read",
82 "usergroup.read",
82 "hg.repogroup.create.false",
83 "hg.repogroup.create.false",
83 "hg.create.none",
84 "hg.create.none",
84 "hg.password_reset.enabled",
85 "hg.password_reset.enabled",
85 "hg.extern_activate.manual",
86 "hg.extern_activate.manual",
86 "hg.create.write_on_repogroup.false",
87 "hg.create.write_on_repogroup.false",
87 "hg.usergroup.create.false",
88 "hg.usergroup.create.false",
88 "group.none",
89 "group.none",
89 "repository.none",
90 "repository.none",
90 "hg.register.none",
91 "hg.register.none",
91 "hg.fork.repository"
92 "hg.fork.repository"
92 ],
93 ],
93 "repositories": { "username/example": "repository.write"},
94 "repositories": { "username/example": "repository.write"},
94 "repositories_groups": { "user-group/repo": "group.none" },
95 "repositories_groups": { "user-group/repo": "group.none" },
95 "user_groups": { "user_group_name": "usergroup.read" }
96 "user_groups": { "user_group_name": "usergroup.read" }
96 },
97 },
97 "user_id": 32,
98 "user_id": 32,
98 "username": "username"
99 "username": "username"
99 }
100 }
100 }
101 }
101 """
102 """
102
103
103 if not has_superadmin_permission(apiuser):
104 if not has_superadmin_permission(apiuser):
104 # make sure normal user does not pass someone else userid,
105 # make sure normal user does not pass someone else userid,
105 # he is not allowed to do that
106 # he is not allowed to do that
106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
107 if not isinstance(userid, Optional) and userid != apiuser.user_id:
107 raise JSONRPCError('userid is not the same as your user')
108 raise JSONRPCError('userid is not the same as your user')
108
109
109 userid = Optional.extract(userid, evaluate_locals=locals())
110 userid = Optional.extract(userid, evaluate_locals=locals())
110 userid = getattr(userid, 'user_id', userid)
111 userid = getattr(userid, 'user_id', userid)
111
112
112 user = get_user_or_error(userid)
113 user = get_user_or_error(userid)
113 data = user.get_api_data(include_secrets=True)
114 data = user.get_api_data(include_secrets=True)
114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
115 data['permissions'] = AuthUser(user_id=user.user_id).permissions
115 return data
116 return data
116
117
117
118
118 @jsonrpc_method()
119 @jsonrpc_method()
119 def get_users(request, apiuser):
120 def get_users(request, apiuser):
120 """
121 """
121 Lists all users in the |RCE| user database.
122 Lists all users in the |RCE| user database.
122
123
123 This command can only be run using an |authtoken| with admin rights to
124 This command can only be run using an |authtoken| with admin rights to
124 the specified repository.
125 the specified repository.
125
126
126 This command takes the following options:
127 This command takes the following options:
127
128
128 :param apiuser: This is filled automatically from the |authtoken|.
129 :param apiuser: This is filled automatically from the |authtoken|.
129 :type apiuser: AuthUser
130 :type apiuser: AuthUser
130
131
131 Example output:
132 Example output:
132
133
133 .. code-block:: bash
134 .. code-block:: bash
134
135
135 id : <id_given_in_input>
136 id : <id_given_in_input>
136 result: [<user_object>, ...]
137 result: [<user_object>, ...]
137 error: null
138 error: null
138 """
139 """
139
140
140 if not has_superadmin_permission(apiuser):
141 if not has_superadmin_permission(apiuser):
141 raise JSONRPCForbidden()
142 raise JSONRPCForbidden()
142
143
143 result = []
144 result = []
144 users_list = User.query().order_by(User.username) \
145 users_list = User.query().order_by(User.username) \
145 .filter(User.username != User.DEFAULT_USER) \
146 .filter(User.username != User.DEFAULT_USER) \
146 .all()
147 .all()
147 for user in users_list:
148 for user in users_list:
148 result.append(user.get_api_data(include_secrets=True))
149 result.append(user.get_api_data(include_secrets=True))
149 return result
150 return result
150
151
151
152
152 @jsonrpc_method()
153 @jsonrpc_method()
153 def create_user(request, apiuser, username, email, password=Optional(''),
154 def create_user(request, apiuser, username, email, password=Optional(''),
154 firstname=Optional(''), lastname=Optional(''),
155 firstname=Optional(''), lastname=Optional(''),
155 active=Optional(True), admin=Optional(False),
156 active=Optional(True), admin=Optional(False),
156 extern_name=Optional('rhodecode'),
157 extern_name=Optional('rhodecode'),
157 extern_type=Optional('rhodecode'),
158 extern_type=Optional('rhodecode'),
158 force_password_change=Optional(False),
159 force_password_change=Optional(False),
159 create_personal_repo_group=Optional(None)):
160 create_personal_repo_group=Optional(None)):
160 """
161 """
161 Creates a new user and returns the new user object.
162 Creates a new user and returns the new user object.
162
163
163 This command can only be run using an |authtoken| with admin rights to
164 This command can only be run using an |authtoken| with admin rights to
164 the specified repository.
165 the specified repository.
165
166
166 This command takes the following options:
167 This command takes the following options:
167
168
168 :param apiuser: This is filled automatically from the |authtoken|.
169 :param apiuser: This is filled automatically from the |authtoken|.
169 :type apiuser: AuthUser
170 :type apiuser: AuthUser
170 :param username: Set the new username.
171 :param username: Set the new username.
171 :type username: str or int
172 :type username: str or int
172 :param email: Set the user email address.
173 :param email: Set the user email address.
173 :type email: str
174 :type email: str
174 :param password: Set the new user password.
175 :param password: Set the new user password.
175 :type password: Optional(str)
176 :type password: Optional(str)
176 :param firstname: Set the new user firstname.
177 :param firstname: Set the new user firstname.
177 :type firstname: Optional(str)
178 :type firstname: Optional(str)
178 :param lastname: Set the new user surname.
179 :param lastname: Set the new user surname.
179 :type lastname: Optional(str)
180 :type lastname: Optional(str)
180 :param active: Set the user as active.
181 :param active: Set the user as active.
181 :type active: Optional(``True`` | ``False``)
182 :type active: Optional(``True`` | ``False``)
182 :param admin: Give the new user admin rights.
183 :param admin: Give the new user admin rights.
183 :type admin: Optional(``True`` | ``False``)
184 :type admin: Optional(``True`` | ``False``)
184 :param extern_name: Set the authentication plugin name.
185 :param extern_name: Set the authentication plugin name.
185 Using LDAP this is filled with LDAP UID.
186 Using LDAP this is filled with LDAP UID.
186 :type extern_name: Optional(str)
187 :type extern_name: Optional(str)
187 :param extern_type: Set the new user authentication plugin.
188 :param extern_type: Set the new user authentication plugin.
188 :type extern_type: Optional(str)
189 :type extern_type: Optional(str)
189 :param force_password_change: Force the new user to change password
190 :param force_password_change: Force the new user to change password
190 on next login.
191 on next login.
191 :type force_password_change: Optional(``True`` | ``False``)
192 :type force_password_change: Optional(``True`` | ``False``)
192 :param create_personal_repo_group: Create personal repo group for this user
193 :param create_personal_repo_group: Create personal repo group for this user
193 :type create_personal_repo_group: Optional(``True`` | ``False``)
194 :type create_personal_repo_group: Optional(``True`` | ``False``)
194 Example output:
195 Example output:
195
196
196 .. code-block:: bash
197 .. code-block:: bash
197
198
198 id : <id_given_in_input>
199 id : <id_given_in_input>
199 result: {
200 result: {
200 "msg" : "created new user `<username>`",
201 "msg" : "created new user `<username>`",
201 "user": <user_obj>
202 "user": <user_obj>
202 }
203 }
203 error: null
204 error: null
204
205
205 Example error output:
206 Example error output:
206
207
207 .. code-block:: bash
208 .. code-block:: bash
208
209
209 id : <id_given_in_input>
210 id : <id_given_in_input>
210 result : null
211 result : null
211 error : {
212 error : {
212 "user `<username>` already exist"
213 "user `<username>` already exist"
213 or
214 or
214 "email `<email>` already exist"
215 "email `<email>` already exist"
215 or
216 or
216 "failed to create user `<username>`"
217 "failed to create user `<username>`"
217 }
218 }
218
219
219 """
220 """
220 if not has_superadmin_permission(apiuser):
221 if not has_superadmin_permission(apiuser):
221 raise JSONRPCForbidden()
222 raise JSONRPCForbidden()
222
223
223 if UserModel().get_by_username(username):
224 if UserModel().get_by_username(username):
224 raise JSONRPCError("user `%s` already exist" % (username,))
225 raise JSONRPCError("user `%s` already exist" % (username,))
225
226
226 if UserModel().get_by_email(email, case_insensitive=True):
227 if UserModel().get_by_email(email, case_insensitive=True):
227 raise JSONRPCError("email `%s` already exist" % (email,))
228 raise JSONRPCError("email `%s` already exist" % (email,))
228
229
229 # generate random password if we actually given the
230 # generate random password if we actually given the
230 # extern_name and it's not rhodecode
231 # extern_name and it's not rhodecode
231 if (not isinstance(extern_name, Optional) and
232 if (not isinstance(extern_name, Optional) and
232 Optional.extract(extern_name) != 'rhodecode'):
233 Optional.extract(extern_name) != 'rhodecode'):
233 # generate temporary password if user is external
234 # generate temporary password if user is external
234 password = PasswordGenerator().gen_password(length=16)
235 password = PasswordGenerator().gen_password(length=16)
235 create_repo_group = Optional.extract(create_personal_repo_group)
236 create_repo_group = Optional.extract(create_personal_repo_group)
236 if isinstance(create_repo_group, basestring):
237 if isinstance(create_repo_group, basestring):
237 create_repo_group = str2bool(create_repo_group)
238 create_repo_group = str2bool(create_repo_group)
238
239
239 try:
240 try:
240 user = UserModel().create_or_update(
241 user = UserModel().create_or_update(
241 username=Optional.extract(username),
242 username=Optional.extract(username),
242 password=Optional.extract(password),
243 password=Optional.extract(password),
243 email=Optional.extract(email),
244 email=Optional.extract(email),
244 firstname=Optional.extract(firstname),
245 firstname=Optional.extract(firstname),
245 lastname=Optional.extract(lastname),
246 lastname=Optional.extract(lastname),
246 active=Optional.extract(active),
247 active=Optional.extract(active),
247 admin=Optional.extract(admin),
248 admin=Optional.extract(admin),
248 extern_type=Optional.extract(extern_type),
249 extern_type=Optional.extract(extern_type),
249 extern_name=Optional.extract(extern_name),
250 extern_name=Optional.extract(extern_name),
250 force_password_change=Optional.extract(force_password_change),
251 force_password_change=Optional.extract(force_password_change),
251 create_repo_group=create_repo_group
252 create_repo_group=create_repo_group
252 )
253 )
253 Session().commit()
254 Session().commit()
254 return {
255 return {
255 'msg': 'created new user `%s`' % username,
256 'msg': 'created new user `%s`' % username,
256 'user': user.get_api_data(include_secrets=True)
257 'user': user.get_api_data(include_secrets=True)
257 }
258 }
258 except Exception:
259 except Exception:
259 log.exception('Error occurred during creation of user')
260 log.exception('Error occurred during creation of user')
260 raise JSONRPCError('failed to create user `%s`' % (username,))
261 raise JSONRPCError('failed to create user `%s`' % (username,))
261
262
262
263
263 @jsonrpc_method()
264 @jsonrpc_method()
264 def update_user(request, apiuser, userid, username=Optional(None),
265 def update_user(request, apiuser, userid, username=Optional(None),
265 email=Optional(None), password=Optional(None),
266 email=Optional(None), password=Optional(None),
266 firstname=Optional(None), lastname=Optional(None),
267 firstname=Optional(None), lastname=Optional(None),
267 active=Optional(None), admin=Optional(None),
268 active=Optional(None), admin=Optional(None),
268 extern_type=Optional(None), extern_name=Optional(None), ):
269 extern_type=Optional(None), extern_name=Optional(None), ):
269 """
270 """
270 Updates the details for the specified user, if that user exists.
271 Updates the details for the specified user, if that user exists.
271
272
272 This command can only be run using an |authtoken| with admin rights to
273 This command can only be run using an |authtoken| with admin rights to
273 the specified repository.
274 the specified repository.
274
275
275 This command takes the following options:
276 This command takes the following options:
276
277
277 :param apiuser: This is filled automatically from |authtoken|.
278 :param apiuser: This is filled automatically from |authtoken|.
278 :type apiuser: AuthUser
279 :type apiuser: AuthUser
279 :param userid: Set the ``userid`` to update.
280 :param userid: Set the ``userid`` to update.
280 :type userid: str or int
281 :type userid: str or int
281 :param username: Set the new username.
282 :param username: Set the new username.
282 :type username: str or int
283 :type username: str or int
283 :param email: Set the new email.
284 :param email: Set the new email.
284 :type email: str
285 :type email: str
285 :param password: Set the new password.
286 :param password: Set the new password.
286 :type password: Optional(str)
287 :type password: Optional(str)
287 :param firstname: Set the new first name.
288 :param firstname: Set the new first name.
288 :type firstname: Optional(str)
289 :type firstname: Optional(str)
289 :param lastname: Set the new surname.
290 :param lastname: Set the new surname.
290 :type lastname: Optional(str)
291 :type lastname: Optional(str)
291 :param active: Set the new user as active.
292 :param active: Set the new user as active.
292 :type active: Optional(``True`` | ``False``)
293 :type active: Optional(``True`` | ``False``)
293 :param admin: Give the user admin rights.
294 :param admin: Give the user admin rights.
294 :type admin: Optional(``True`` | ``False``)
295 :type admin: Optional(``True`` | ``False``)
295 :param extern_name: Set the authentication plugin user name.
296 :param extern_name: Set the authentication plugin user name.
296 Using LDAP this is filled with LDAP UID.
297 Using LDAP this is filled with LDAP UID.
297 :type extern_name: Optional(str)
298 :type extern_name: Optional(str)
298 :param extern_type: Set the authentication plugin type.
299 :param extern_type: Set the authentication plugin type.
299 :type extern_type: Optional(str)
300 :type extern_type: Optional(str)
300
301
301
302
302 Example output:
303 Example output:
303
304
304 .. code-block:: bash
305 .. code-block:: bash
305
306
306 id : <id_given_in_input>
307 id : <id_given_in_input>
307 result: {
308 result: {
308 "msg" : "updated user ID:<userid> <username>",
309 "msg" : "updated user ID:<userid> <username>",
309 "user": <user_object>,
310 "user": <user_object>,
310 }
311 }
311 error: null
312 error: null
312
313
313 Example error output:
314 Example error output:
314
315
315 .. code-block:: bash
316 .. code-block:: bash
316
317
317 id : <id_given_in_input>
318 id : <id_given_in_input>
318 result : null
319 result : null
319 error : {
320 error : {
320 "failed to update user `<username>`"
321 "failed to update user `<username>`"
321 }
322 }
322
323
323 """
324 """
324 if not has_superadmin_permission(apiuser):
325 if not has_superadmin_permission(apiuser):
325 raise JSONRPCForbidden()
326 raise JSONRPCForbidden()
326
327
327 user = get_user_or_error(userid)
328 user = get_user_or_error(userid)
328
329
329 # only non optional arguments will be stored in updates
330 # only non optional arguments will be stored in updates
330 updates = {}
331 updates = {}
331
332
332 try:
333 try:
333
334
334 store_update(updates, username, 'username')
335 store_update(updates, username, 'username')
335 store_update(updates, password, 'password')
336 store_update(updates, password, 'password')
336 store_update(updates, email, 'email')
337 store_update(updates, email, 'email')
337 store_update(updates, firstname, 'name')
338 store_update(updates, firstname, 'name')
338 store_update(updates, lastname, 'lastname')
339 store_update(updates, lastname, 'lastname')
339 store_update(updates, active, 'active')
340 store_update(updates, active, 'active')
340 store_update(updates, admin, 'admin')
341 store_update(updates, admin, 'admin')
341 store_update(updates, extern_name, 'extern_name')
342 store_update(updates, extern_name, 'extern_name')
342 store_update(updates, extern_type, 'extern_type')
343 store_update(updates, extern_type, 'extern_type')
343
344
344 user = UserModel().update_user(user, **updates)
345 user = UserModel().update_user(user, **updates)
345 Session().commit()
346 Session().commit()
346 return {
347 return {
347 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
348 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
348 'user': user.get_api_data(include_secrets=True)
349 'user': user.get_api_data(include_secrets=True)
349 }
350 }
350 except DefaultUserException:
351 except DefaultUserException:
351 log.exception("Default user edit exception")
352 log.exception("Default user edit exception")
352 raise JSONRPCError('editing default user is forbidden')
353 raise JSONRPCError('editing default user is forbidden')
353 except Exception:
354 except Exception:
354 log.exception("Error occurred during update of user")
355 log.exception("Error occurred during update of user")
355 raise JSONRPCError('failed to update user `%s`' % (userid,))
356 raise JSONRPCError('failed to update user `%s`' % (userid,))
356
357
357
358
358 @jsonrpc_method()
359 @jsonrpc_method()
359 def delete_user(request, apiuser, userid):
360 def delete_user(request, apiuser, userid):
360 """
361 """
361 Deletes the specified user from the |RCE| user database.
362 Deletes the specified user from the |RCE| user database.
362
363
363 This command can only be run using an |authtoken| with admin rights to
364 This command can only be run using an |authtoken| with admin rights to
364 the specified repository.
365 the specified repository.
365
366
366 .. important::
367 .. important::
367
368
368 Ensure all open pull requests and open code review
369 Ensure all open pull requests and open code review
369 requests to this user are close.
370 requests to this user are close.
370
371
371 Also ensure all repositories, or repository groups owned by this
372 Also ensure all repositories, or repository groups owned by this
372 user are reassigned before deletion.
373 user are reassigned before deletion.
373
374
374 This command takes the following options:
375 This command takes the following options:
375
376
376 :param apiuser: This is filled automatically from the |authtoken|.
377 :param apiuser: This is filled automatically from the |authtoken|.
377 :type apiuser: AuthUser
378 :type apiuser: AuthUser
378 :param userid: Set the user to delete.
379 :param userid: Set the user to delete.
379 :type userid: str or int
380 :type userid: str or int
380
381
381 Example output:
382 Example output:
382
383
383 .. code-block:: bash
384 .. code-block:: bash
384
385
385 id : <id_given_in_input>
386 id : <id_given_in_input>
386 result: {
387 result: {
387 "msg" : "deleted user ID:<userid> <username>",
388 "msg" : "deleted user ID:<userid> <username>",
388 "user": null
389 "user": null
389 }
390 }
390 error: null
391 error: null
391
392
392 Example error output:
393 Example error output:
393
394
394 .. code-block:: bash
395 .. code-block:: bash
395
396
396 id : <id_given_in_input>
397 id : <id_given_in_input>
397 result : null
398 result : null
398 error : {
399 error : {
399 "failed to delete user ID:<userid> <username>"
400 "failed to delete user ID:<userid> <username>"
400 }
401 }
401
402
402 """
403 """
403 if not has_superadmin_permission(apiuser):
404 if not has_superadmin_permission(apiuser):
404 raise JSONRPCForbidden()
405 raise JSONRPCForbidden()
405
406
406 user = get_user_or_error(userid)
407 user = get_user_or_error(userid)
407
408
408 try:
409 try:
409 UserModel().delete(userid)
410 UserModel().delete(userid)
410 Session().commit()
411 Session().commit()
411 return {
412 return {
412 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
413 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
413 'user': None
414 'user': None
414 }
415 }
415 except Exception:
416 except Exception:
416 log.exception("Error occurred during deleting of user")
417 log.exception("Error occurred during deleting of user")
417 raise JSONRPCError(
418 raise JSONRPCError(
418 'failed to delete user ID:%s %s' % (user.user_id, user.username))
419 'failed to delete user ID:%s %s' % (user.user_id, user.username))
419
420
420
421
421 @jsonrpc_method()
422 @jsonrpc_method()
422 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
423 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
423 """
424 """
424 Displays all repositories locked by the specified user.
425 Displays all repositories locked by the specified user.
425
426
426 * If this command is run by a non-admin user, it returns
427 * If this command is run by a non-admin user, it returns
427 a list of |repos| locked by that user.
428 a list of |repos| locked by that user.
428
429
429 This command takes the following options:
430 This command takes the following options:
430
431
431 :param apiuser: This is filled automatically from the |authtoken|.
432 :param apiuser: This is filled automatically from the |authtoken|.
432 :type apiuser: AuthUser
433 :type apiuser: AuthUser
433 :param userid: Sets the userid whose list of locked |repos| will be
434 :param userid: Sets the userid whose list of locked |repos| will be
434 displayed.
435 displayed.
435 :type userid: Optional(str or int)
436 :type userid: Optional(str or int)
436
437
437 Example output:
438 Example output:
438
439
439 .. code-block:: bash
440 .. code-block:: bash
440
441
441 id : <id_given_in_input>
442 id : <id_given_in_input>
442 result : {
443 result : {
443 [repo_object, repo_object,...]
444 [repo_object, repo_object,...]
444 }
445 }
445 error : null
446 error : null
446 """
447 """
447
448
448 include_secrets = False
449 include_secrets = False
449 if not has_superadmin_permission(apiuser):
450 if not has_superadmin_permission(apiuser):
450 # make sure normal user does not pass someone else userid,
451 # make sure normal user does not pass someone else userid,
451 # he is not allowed to do that
452 # he is not allowed to do that
452 if not isinstance(userid, Optional) and userid != apiuser.user_id:
453 if not isinstance(userid, Optional) and userid != apiuser.user_id:
453 raise JSONRPCError('userid is not the same as your user')
454 raise JSONRPCError('userid is not the same as your user')
454 else:
455 else:
455 include_secrets = True
456 include_secrets = True
456
457
457 userid = Optional.extract(userid, evaluate_locals=locals())
458 userid = Optional.extract(userid, evaluate_locals=locals())
458 userid = getattr(userid, 'user_id', userid)
459 userid = getattr(userid, 'user_id', userid)
459 user = get_user_or_error(userid)
460 user = get_user_or_error(userid)
460
461
461 ret = []
462 ret = []
462
463
463 # show all locks
464 # show all locks
464 for r in Repository.getAll():
465 for r in Repository.getAll():
465 _user_id, _time, _reason = r.locked
466 _user_id, _time, _reason = r.locked
466 if _user_id and _time:
467 if _user_id and _time:
467 _api_data = r.get_api_data(include_secrets=include_secrets)
468 _api_data = r.get_api_data(include_secrets=include_secrets)
468 # if we use user filter just show the locks for this user
469 # if we use user filter just show the locks for this user
469 if safe_int(_user_id) == user.user_id:
470 if safe_int(_user_id) == user.user_id:
470 ret.append(_api_data)
471 ret.append(_api_data)
471
472
472 return ret
473 return ret
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,246 +1,247 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 import pytest
21 import pytest
22
22
23 from rhodecode.lib import helpers as h
23 from rhodecode.lib import helpers as h
24 from rhodecode.model.db import User, UserFollowing, Repository
24 from rhodecode.model.db import User, UserFollowing, Repository
25 from rhodecode.tests import (
25 from rhodecode.tests import (
26 TestController, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
26 TestController, url, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_EMAIL,
27 assert_session_flash)
27 assert_session_flash)
28 from rhodecode.tests.fixture import Fixture
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import AssertResponse
29 from rhodecode.tests.utils import AssertResponse
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33
33
34 class TestMyAccountController(TestController):
34 class TestMyAccountController(TestController):
35 test_user_1 = 'testme'
35 test_user_1 = 'testme'
36 test_user_1_password = '0jd83nHNS/d23n'
36 test_user_1_password = '0jd83nHNS/d23n'
37 destroy_users = set()
37 destroy_users = set()
38
38
39 @classmethod
39 @classmethod
40 def teardown_class(cls):
40 def teardown_class(cls):
41 fixture.destroy_users(cls.destroy_users)
41 fixture.destroy_users(cls.destroy_users)
42
42
43 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
43 def test_logout_form_contains_csrf(self, autologin_user, csrf_token):
44 response = self.app.get(url('home'))
44 response = self.app.get(url('home'))
45 assert_response = AssertResponse(response)
45 assert_response = AssertResponse(response)
46 element = assert_response.get_element('.logout #csrf_token')
46 element = assert_response.get_element('.logout #csrf_token')
47 assert element.value == csrf_token
47 assert element.value == csrf_token
48
48
49 def test_my_account_edit(self):
49 def test_my_account_edit(self):
50 self.log_user()
50 self.log_user()
51 response = self.app.get(url('my_account_edit'))
51 response = self.app.get(url('my_account_edit'))
52
52
53 response.mustcontain('value="test_admin')
53 response.mustcontain('value="test_admin')
54
54
55 def test_my_account_my_repos(self):
55 def test_my_account_my_repos(self):
56 self.log_user()
56 self.log_user()
57 response = self.app.get(url('my_account_repos'))
57 response = self.app.get(url('my_account_repos'))
58 repos = Repository.query().filter(
58 repos = Repository.query().filter(
59 Repository.user == User.get_by_username(
59 Repository.user == User.get_by_username(
60 TEST_USER_ADMIN_LOGIN)).all()
60 TEST_USER_ADMIN_LOGIN)).all()
61 for repo in repos:
61 for repo in repos:
62 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
62 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
63
63
64 def test_my_account_my_watched(self):
64 def test_my_account_my_watched(self):
65 self.log_user()
65 self.log_user()
66 response = self.app.get(url('my_account_watched'))
66 response = self.app.get(url('my_account_watched'))
67
67
68 repos = UserFollowing.query().filter(
68 repos = UserFollowing.query().filter(
69 UserFollowing.user == User.get_by_username(
69 UserFollowing.user == User.get_by_username(
70 TEST_USER_ADMIN_LOGIN)).all()
70 TEST_USER_ADMIN_LOGIN)).all()
71 for repo in repos:
71 for repo in repos:
72 response.mustcontain(
72 response.mustcontain(
73 '"name_raw": "%s"' % repo.follows_repository.repo_name)
73 '"name_raw": "%s"' % repo.follows_repository.repo_name)
74
74
75 @pytest.mark.backends("git", "hg")
75 @pytest.mark.backends("git", "hg")
76 def test_my_account_my_pullrequests(self, pr_util):
76 def test_my_account_my_pullrequests(self, pr_util):
77 self.log_user()
77 self.log_user()
78 response = self.app.get(url('my_account_pullrequests'))
78 response = self.app.get(url('my_account_pullrequests'))
79 response.mustcontain('There are currently no open pull '
79 response.mustcontain('There are currently no open pull '
80 'requests requiring your participation.')
80 'requests requiring your participation.')
81
81
82 pr = pr_util.create_pull_request(title='TestMyAccountPR')
82 pr = pr_util.create_pull_request(title='TestMyAccountPR')
83 response = self.app.get(url('my_account_pullrequests'))
83 response = self.app.get(url('my_account_pullrequests'))
84 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
84 response.mustcontain('"name_raw": %s' % pr.pull_request_id)
85 response.mustcontain('TestMyAccountPR')
85 response.mustcontain('TestMyAccountPR')
86
86
87 def test_my_account_my_emails(self):
87 def test_my_account_my_emails(self):
88 self.log_user()
88 self.log_user()
89 response = self.app.get(url('my_account_emails'))
89 response = self.app.get(url('my_account_emails'))
90 response.mustcontain('No additional emails specified')
90 response.mustcontain('No additional emails specified')
91
91
92 def test_my_account_my_emails_add_existing_email(self):
92 def test_my_account_my_emails_add_existing_email(self):
93 self.log_user()
93 self.log_user()
94 response = self.app.get(url('my_account_emails'))
94 response = self.app.get(url('my_account_emails'))
95 response.mustcontain('No additional emails specified')
95 response.mustcontain('No additional emails specified')
96 response = self.app.post(url('my_account_emails'),
96 response = self.app.post(url('my_account_emails'),
97 {'new_email': TEST_USER_REGULAR_EMAIL,
97 {'new_email': TEST_USER_REGULAR_EMAIL,
98 'csrf_token': self.csrf_token})
98 'csrf_token': self.csrf_token})
99 assert_session_flash(response, 'This e-mail address is already taken')
99 assert_session_flash(response, 'This e-mail address is already taken')
100
100
101 def test_my_account_my_emails_add_mising_email_in_form(self):
101 def test_my_account_my_emails_add_mising_email_in_form(self):
102 self.log_user()
102 self.log_user()
103 response = self.app.get(url('my_account_emails'))
103 response = self.app.get(url('my_account_emails'))
104 response.mustcontain('No additional emails specified')
104 response.mustcontain('No additional emails specified')
105 response = self.app.post(url('my_account_emails'),
105 response = self.app.post(url('my_account_emails'),
106 {'csrf_token': self.csrf_token})
106 {'csrf_token': self.csrf_token})
107 assert_session_flash(response, 'Please enter an email address')
107 assert_session_flash(response, 'Please enter an email address')
108
108
109 def test_my_account_my_emails_add_remove(self):
109 def test_my_account_my_emails_add_remove(self):
110 self.log_user()
110 self.log_user()
111 response = self.app.get(url('my_account_emails'))
111 response = self.app.get(url('my_account_emails'))
112 response.mustcontain('No additional emails specified')
112 response.mustcontain('No additional emails specified')
113
113
114 response = self.app.post(url('my_account_emails'),
114 response = self.app.post(url('my_account_emails'),
115 {'new_email': 'foo@barz.com',
115 {'new_email': 'foo@barz.com',
116 'csrf_token': self.csrf_token})
116 'csrf_token': self.csrf_token})
117
117
118 response = self.app.get(url('my_account_emails'))
118 response = self.app.get(url('my_account_emails'))
119
119
120 from rhodecode.model.db import UserEmailMap
120 from rhodecode.model.db import UserEmailMap
121 email_id = UserEmailMap.query().filter(
121 email_id = UserEmailMap.query().filter(
122 UserEmailMap.user == User.get_by_username(
122 UserEmailMap.user == User.get_by_username(
123 TEST_USER_ADMIN_LOGIN)).filter(
123 TEST_USER_ADMIN_LOGIN)).filter(
124 UserEmailMap.email == 'foo@barz.com').one().email_id
124 UserEmailMap.email == 'foo@barz.com').one().email_id
125
125
126 response.mustcontain('foo@barz.com')
126 response.mustcontain('foo@barz.com')
127 response.mustcontain('<input id="del_email_id" name="del_email_id" '
127 response.mustcontain('<input id="del_email_id" name="del_email_id" '
128 'type="hidden" value="%s" />' % email_id)
128 'type="hidden" value="%s" />' % email_id)
129
129
130 response = self.app.post(
130 response = self.app.post(
131 url('my_account_emails'), {
131 url('my_account_emails'), {
132 'del_email_id': email_id, '_method': 'delete',
132 'del_email_id': email_id, '_method': 'delete',
133 'csrf_token': self.csrf_token})
133 'csrf_token': self.csrf_token})
134 assert_session_flash(response, 'Removed email address from user account')
134 assert_session_flash(response, 'Removed email address from user account')
135 response = self.app.get(url('my_account_emails'))
135 response = self.app.get(url('my_account_emails'))
136 response.mustcontain('No additional emails specified')
136 response.mustcontain('No additional emails specified')
137
137
138 @pytest.mark.parametrize(
138 @pytest.mark.parametrize(
139 "name, attrs", [
139 "name, attrs", [
140 ('firstname', {'firstname': 'new_username'}),
140 ('firstname', {'firstname': 'new_username'}),
141 ('lastname', {'lastname': 'new_username'}),
141 ('lastname', {'lastname': 'new_username'}),
142 ('admin', {'admin': True}),
142 ('admin', {'admin': True}),
143 ('admin', {'admin': False}),
143 ('admin', {'admin': False}),
144 ('extern_type', {'extern_type': 'ldap'}),
144 ('extern_type', {'extern_type': 'ldap'}),
145 ('extern_type', {'extern_type': None}),
145 ('extern_type', {'extern_type': None}),
146 # ('extern_name', {'extern_name': 'test'}),
146 # ('extern_name', {'extern_name': 'test'}),
147 # ('extern_name', {'extern_name': None}),
147 # ('extern_name', {'extern_name': None}),
148 ('active', {'active': False}),
148 ('active', {'active': False}),
149 ('active', {'active': True}),
149 ('active', {'active': True}),
150 ('email', {'email': 'some@email.com'}),
150 ('email', {'email': 'some@email.com'}),
151 ])
151 ])
152 def test_my_account_update(self, name, attrs):
152 def test_my_account_update(self, name, attrs):
153 usr = fixture.create_user(self.test_user_1,
153 usr = fixture.create_user(self.test_user_1,
154 password=self.test_user_1_password,
154 password=self.test_user_1_password,
155 email='testme@rhodecode.org',
155 email='testme@rhodecode.org',
156 extern_type='rhodecode',
156 extern_type='rhodecode',
157 extern_name=self.test_user_1,
157 extern_name=self.test_user_1,
158 skip_if_exists=True)
158 skip_if_exists=True)
159 self.destroy_users.add(self.test_user_1)
159 self.destroy_users.add(self.test_user_1)
160
160
161 params = usr.get_api_data() # current user data
161 params = usr.get_api_data() # current user data
162 user_id = usr.user_id
162 user_id = usr.user_id
163 self.log_user(
163 self.log_user(
164 username=self.test_user_1, password=self.test_user_1_password)
164 username=self.test_user_1, password=self.test_user_1_password)
165
165
166 params.update({'password_confirmation': ''})
166 params.update({'password_confirmation': ''})
167 params.update({'new_password': ''})
167 params.update({'new_password': ''})
168 params.update({'extern_type': 'rhodecode'})
168 params.update({'extern_type': 'rhodecode'})
169 params.update({'extern_name': self.test_user_1})
169 params.update({'extern_name': self.test_user_1})
170 params.update({'csrf_token': self.csrf_token})
170 params.update({'csrf_token': self.csrf_token})
171
171
172 params.update(attrs)
172 params.update(attrs)
173 # my account page cannot set language param yet, only for admins
173 # my account page cannot set language param yet, only for admins
174 del params['language']
174 del params['language']
175 response = self.app.post(url('my_account'), params)
175 response = self.app.post(url('my_account'), params)
176
176
177 assert_session_flash(
177 assert_session_flash(
178 response, 'Your account was updated successfully')
178 response, 'Your account was updated successfully')
179
179
180 del params['csrf_token']
180 del params['csrf_token']
181
181
182 updated_user = User.get_by_username(self.test_user_1)
182 updated_user = User.get_by_username(self.test_user_1)
183 updated_params = updated_user.get_api_data()
183 updated_params = updated_user.get_api_data()
184 updated_params.update({'password_confirmation': ''})
184 updated_params.update({'password_confirmation': ''})
185 updated_params.update({'new_password': ''})
185 updated_params.update({'new_password': ''})
186
186
187 params['last_login'] = updated_params['last_login']
187 params['last_login'] = updated_params['last_login']
188 params['last_activity'] = updated_params['last_activity']
188 # my account page cannot set language param yet, only for admins
189 # my account page cannot set language param yet, only for admins
189 # but we get this info from API anyway
190 # but we get this info from API anyway
190 params['language'] = updated_params['language']
191 params['language'] = updated_params['language']
191
192
192 if name == 'email':
193 if name == 'email':
193 params['emails'] = [attrs['email']]
194 params['emails'] = [attrs['email']]
194 if name == 'extern_type':
195 if name == 'extern_type':
195 # cannot update this via form, expected value is original one
196 # cannot update this via form, expected value is original one
196 params['extern_type'] = "rhodecode"
197 params['extern_type'] = "rhodecode"
197 if name == 'extern_name':
198 if name == 'extern_name':
198 # cannot update this via form, expected value is original one
199 # cannot update this via form, expected value is original one
199 params['extern_name'] = str(user_id)
200 params['extern_name'] = str(user_id)
200 if name == 'active':
201 if name == 'active':
201 # my account cannot deactivate account
202 # my account cannot deactivate account
202 params['active'] = True
203 params['active'] = True
203 if name == 'admin':
204 if name == 'admin':
204 # my account cannot make you an admin !
205 # my account cannot make you an admin !
205 params['admin'] = False
206 params['admin'] = False
206
207
207 assert params == updated_params
208 assert params == updated_params
208
209
209 def test_my_account_update_err_email_exists(self):
210 def test_my_account_update_err_email_exists(self):
210 self.log_user()
211 self.log_user()
211
212
212 new_email = 'test_regular@mail.com' # already exisitn email
213 new_email = 'test_regular@mail.com' # already exisitn email
213 response = self.app.post(url('my_account'),
214 response = self.app.post(url('my_account'),
214 params={
215 params={
215 'username': 'test_admin',
216 'username': 'test_admin',
216 'new_password': 'test12',
217 'new_password': 'test12',
217 'password_confirmation': 'test122',
218 'password_confirmation': 'test122',
218 'firstname': 'NewName',
219 'firstname': 'NewName',
219 'lastname': 'NewLastname',
220 'lastname': 'NewLastname',
220 'email': new_email,
221 'email': new_email,
221 'csrf_token': self.csrf_token,
222 'csrf_token': self.csrf_token,
222 })
223 })
223
224
224 response.mustcontain('This e-mail address is already taken')
225 response.mustcontain('This e-mail address is already taken')
225
226
226 def test_my_account_update_err(self):
227 def test_my_account_update_err(self):
227 self.log_user('test_regular2', 'test12')
228 self.log_user('test_regular2', 'test12')
228
229
229 new_email = 'newmail.pl'
230 new_email = 'newmail.pl'
230 response = self.app.post(url('my_account'),
231 response = self.app.post(url('my_account'),
231 params={
232 params={
232 'username': 'test_admin',
233 'username': 'test_admin',
233 'new_password': 'test12',
234 'new_password': 'test12',
234 'password_confirmation': 'test122',
235 'password_confirmation': 'test122',
235 'firstname': 'NewName',
236 'firstname': 'NewName',
236 'lastname': 'NewLastname',
237 'lastname': 'NewLastname',
237 'email': new_email,
238 'email': new_email,
238 'csrf_token': self.csrf_token,
239 'csrf_token': self.csrf_token,
239 })
240 })
240
241
241 response.mustcontain('An email address must contain a single @')
242 response.mustcontain('An email address must contain a single @')
242 from rhodecode.model import validators
243 from rhodecode.model import validators
243 msg = validators.ValidUsername(
244 msg = validators.ValidUsername(
244 edit=False, old_data={})._messages['username_exists']
245 edit=False, old_data={})._messages['username_exists']
245 msg = h.html_escape(msg % {'username': 'test_admin'})
246 msg = h.html_escape(msg % {'username': 'test_admin'})
246 response.mustcontain(u"%s" % msg)
247 response.mustcontain(u"%s" % msg)
General Comments 0
You need to be logged in to leave comments. Login now