##// END OF EJS Templates
user-api: enable per-user audit logs fetching via API endpoint.
marcink -
r1579:bcde1932 default
parent child Browse files
Show More

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

@@ -1,473 +1,514 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from rhodecode.api import jsonrpc_method, JSONRPCError, JSONRPCForbidden
24 24 from rhodecode.api.utils import (
25 25 Optional, OAttr, has_superadmin_permission, get_user_or_error, store_update)
26 26 from rhodecode.lib.auth import AuthUser, PasswordGenerator
27 27 from rhodecode.lib.exceptions import DefaultUserException
28 28 from rhodecode.lib.utils2 import safe_int, str2bool
29 29 from rhodecode.model.db import Session, User, Repository
30 30 from rhodecode.model.user import UserModel
31 31
32
33 32 log = logging.getLogger(__name__)
34 33
35 34
36 35 @jsonrpc_method()
37 36 def get_user(request, apiuser, userid=Optional(OAttr('apiuser'))):
38 37 """
39 38 Returns the information associated with a username or userid.
40 39
41 40 * If the ``userid`` is not set, this command returns the information
42 41 for the ``userid`` calling the method.
43 42
44 43 .. note::
45 44
46 45 Normal users may only run this command against their ``userid``. For
47 46 full privileges you must run this command using an |authtoken| with
48 47 admin rights.
49 48
50 49 :param apiuser: This is filled automatically from the |authtoken|.
51 50 :type apiuser: AuthUser
52 51 :param userid: Sets the userid for which data will be returned.
53 52 :type userid: Optional(str or int)
54 53
55 54 Example output:
56 55
57 56 .. code-block:: bash
58 57
59 58 {
60 59 "error": null,
61 60 "id": <id>,
62 61 "result": {
63 62 "active": true,
64 63 "admin": false,
65 64 "api_keys": [ list of keys ],
66 65 "auth_tokens": [ list of tokens with details ],
67 66 "email": "user@example.com",
68 67 "emails": [
69 68 "user@example.com"
70 69 ],
71 70 "extern_name": "rhodecode",
72 71 "extern_type": "rhodecode",
73 72 "firstname": "username",
74 73 "ip_addresses": [],
75 74 "language": null,
76 75 "last_login": "Timestamp",
77 76 "last_activity": "Timestamp",
78 77 "lastname": "surnae",
79 78 "permissions": {
80 79 "global": [
81 80 "hg.inherit_default_perms.true",
82 81 "usergroup.read",
83 82 "hg.repogroup.create.false",
84 83 "hg.create.none",
85 84 "hg.password_reset.enabled",
86 85 "hg.extern_activate.manual",
87 86 "hg.create.write_on_repogroup.false",
88 87 "hg.usergroup.create.false",
89 88 "group.none",
90 89 "repository.none",
91 90 "hg.register.none",
92 91 "hg.fork.repository"
93 92 ],
94 93 "repositories": { "username/example": "repository.write"},
95 94 "repositories_groups": { "user-group/repo": "group.none" },
96 95 "user_groups": { "user_group_name": "usergroup.read" }
97 96 },
98 97 "user_id": 32,
99 98 "username": "username"
100 99 }
101 100 }
102 101 """
103 102
104 103 if not has_superadmin_permission(apiuser):
105 104 # make sure normal user does not pass someone else userid,
106 105 # he is not allowed to do that
107 106 if not isinstance(userid, Optional) and userid != apiuser.user_id:
108 107 raise JSONRPCError('userid is not the same as your user')
109 108
110 109 userid = Optional.extract(userid, evaluate_locals=locals())
111 110 userid = getattr(userid, 'user_id', userid)
112 111
113 112 user = get_user_or_error(userid)
114 113 data = user.get_api_data(include_secrets=True)
115 114 data['permissions'] = AuthUser(user_id=user.user_id).permissions
116 115 return data
117 116
118 117
119 118 @jsonrpc_method()
120 119 def get_users(request, apiuser):
121 120 """
122 121 Lists all users in the |RCE| user database.
123 122
124 123 This command can only be run using an |authtoken| with admin rights to
125 124 the specified repository.
126 125
127 126 This command takes the following options:
128 127
129 128 :param apiuser: This is filled automatically from the |authtoken|.
130 129 :type apiuser: AuthUser
131 130
132 131 Example output:
133 132
134 133 .. code-block:: bash
135 134
136 135 id : <id_given_in_input>
137 136 result: [<user_object>, ...]
138 137 error: null
139 138 """
140 139
141 140 if not has_superadmin_permission(apiuser):
142 141 raise JSONRPCForbidden()
143 142
144 143 result = []
145 144 users_list = User.query().order_by(User.username) \
146 145 .filter(User.username != User.DEFAULT_USER) \
147 146 .all()
148 147 for user in users_list:
149 148 result.append(user.get_api_data(include_secrets=True))
150 149 return result
151 150
152 151
153 152 @jsonrpc_method()
154 153 def create_user(request, apiuser, username, email, password=Optional(''),
155 154 firstname=Optional(''), lastname=Optional(''),
156 155 active=Optional(True), admin=Optional(False),
157 156 extern_name=Optional('rhodecode'),
158 157 extern_type=Optional('rhodecode'),
159 158 force_password_change=Optional(False),
160 159 create_personal_repo_group=Optional(None)):
161 160 """
162 161 Creates a new user and returns the new user object.
163 162
164 163 This command can only be run using an |authtoken| with admin rights to
165 164 the specified repository.
166 165
167 166 This command takes the following options:
168 167
169 168 :param apiuser: This is filled automatically from the |authtoken|.
170 169 :type apiuser: AuthUser
171 170 :param username: Set the new username.
172 171 :type username: str or int
173 172 :param email: Set the user email address.
174 173 :type email: str
175 174 :param password: Set the new user password.
176 175 :type password: Optional(str)
177 176 :param firstname: Set the new user firstname.
178 177 :type firstname: Optional(str)
179 178 :param lastname: Set the new user surname.
180 179 :type lastname: Optional(str)
181 180 :param active: Set the user as active.
182 181 :type active: Optional(``True`` | ``False``)
183 182 :param admin: Give the new user admin rights.
184 183 :type admin: Optional(``True`` | ``False``)
185 184 :param extern_name: Set the authentication plugin name.
186 185 Using LDAP this is filled with LDAP UID.
187 186 :type extern_name: Optional(str)
188 187 :param extern_type: Set the new user authentication plugin.
189 188 :type extern_type: Optional(str)
190 189 :param force_password_change: Force the new user to change password
191 190 on next login.
192 191 :type force_password_change: Optional(``True`` | ``False``)
193 192 :param create_personal_repo_group: Create personal repo group for this user
194 193 :type create_personal_repo_group: Optional(``True`` | ``False``)
195 194 Example output:
196 195
197 196 .. code-block:: bash
198 197
199 198 id : <id_given_in_input>
200 199 result: {
201 200 "msg" : "created new user `<username>`",
202 201 "user": <user_obj>
203 202 }
204 203 error: null
205 204
206 205 Example error output:
207 206
208 207 .. code-block:: bash
209 208
210 209 id : <id_given_in_input>
211 210 result : null
212 211 error : {
213 212 "user `<username>` already exist"
214 213 or
215 214 "email `<email>` already exist"
216 215 or
217 216 "failed to create user `<username>`"
218 217 }
219 218
220 219 """
221 220 if not has_superadmin_permission(apiuser):
222 221 raise JSONRPCForbidden()
223 222
224 223 if UserModel().get_by_username(username):
225 224 raise JSONRPCError("user `%s` already exist" % (username,))
226 225
227 226 if UserModel().get_by_email(email, case_insensitive=True):
228 227 raise JSONRPCError("email `%s` already exist" % (email,))
229 228
230 229 # generate random password if we actually given the
231 230 # extern_name and it's not rhodecode
232 231 if (not isinstance(extern_name, Optional) and
233 232 Optional.extract(extern_name) != 'rhodecode'):
234 233 # generate temporary password if user is external
235 234 password = PasswordGenerator().gen_password(length=16)
236 235 create_repo_group = Optional.extract(create_personal_repo_group)
237 236 if isinstance(create_repo_group, basestring):
238 237 create_repo_group = str2bool(create_repo_group)
239 238
240 239 try:
241 240 user = UserModel().create_or_update(
242 241 username=Optional.extract(username),
243 242 password=Optional.extract(password),
244 243 email=Optional.extract(email),
245 244 firstname=Optional.extract(firstname),
246 245 lastname=Optional.extract(lastname),
247 246 active=Optional.extract(active),
248 247 admin=Optional.extract(admin),
249 248 extern_type=Optional.extract(extern_type),
250 249 extern_name=Optional.extract(extern_name),
251 250 force_password_change=Optional.extract(force_password_change),
252 251 create_repo_group=create_repo_group
253 252 )
254 253 Session().commit()
255 254 return {
256 255 'msg': 'created new user `%s`' % username,
257 256 'user': user.get_api_data(include_secrets=True)
258 257 }
259 258 except Exception:
260 259 log.exception('Error occurred during creation of user')
261 260 raise JSONRPCError('failed to create user `%s`' % (username,))
262 261
263 262
264 263 @jsonrpc_method()
265 264 def update_user(request, apiuser, userid, username=Optional(None),
266 265 email=Optional(None), password=Optional(None),
267 266 firstname=Optional(None), lastname=Optional(None),
268 267 active=Optional(None), admin=Optional(None),
269 268 extern_type=Optional(None), extern_name=Optional(None), ):
270 269 """
271 270 Updates the details for the specified user, if that user exists.
272 271
273 272 This command can only be run using an |authtoken| with admin rights to
274 273 the specified repository.
275 274
276 275 This command takes the following options:
277 276
278 277 :param apiuser: This is filled automatically from |authtoken|.
279 278 :type apiuser: AuthUser
280 279 :param userid: Set the ``userid`` to update.
281 280 :type userid: str or int
282 281 :param username: Set the new username.
283 282 :type username: str or int
284 283 :param email: Set the new email.
285 284 :type email: str
286 285 :param password: Set the new password.
287 286 :type password: Optional(str)
288 287 :param firstname: Set the new first name.
289 288 :type firstname: Optional(str)
290 289 :param lastname: Set the new surname.
291 290 :type lastname: Optional(str)
292 291 :param active: Set the new user as active.
293 292 :type active: Optional(``True`` | ``False``)
294 293 :param admin: Give the user admin rights.
295 294 :type admin: Optional(``True`` | ``False``)
296 295 :param extern_name: Set the authentication plugin user name.
297 296 Using LDAP this is filled with LDAP UID.
298 297 :type extern_name: Optional(str)
299 298 :param extern_type: Set the authentication plugin type.
300 299 :type extern_type: Optional(str)
301 300
302 301
303 302 Example output:
304 303
305 304 .. code-block:: bash
306 305
307 306 id : <id_given_in_input>
308 307 result: {
309 308 "msg" : "updated user ID:<userid> <username>",
310 309 "user": <user_object>,
311 310 }
312 311 error: null
313 312
314 313 Example error output:
315 314
316 315 .. code-block:: bash
317 316
318 317 id : <id_given_in_input>
319 318 result : null
320 319 error : {
321 320 "failed to update user `<username>`"
322 321 }
323 322
324 323 """
325 324 if not has_superadmin_permission(apiuser):
326 325 raise JSONRPCForbidden()
327 326
328 327 user = get_user_or_error(userid)
329 328
330 329 # only non optional arguments will be stored in updates
331 330 updates = {}
332 331
333 332 try:
334 333
335 334 store_update(updates, username, 'username')
336 335 store_update(updates, password, 'password')
337 336 store_update(updates, email, 'email')
338 337 store_update(updates, firstname, 'name')
339 338 store_update(updates, lastname, 'lastname')
340 339 store_update(updates, active, 'active')
341 340 store_update(updates, admin, 'admin')
342 341 store_update(updates, extern_name, 'extern_name')
343 342 store_update(updates, extern_type, 'extern_type')
344 343
345 344 user = UserModel().update_user(user, **updates)
346 345 Session().commit()
347 346 return {
348 347 'msg': 'updated user ID:%s %s' % (user.user_id, user.username),
349 348 'user': user.get_api_data(include_secrets=True)
350 349 }
351 350 except DefaultUserException:
352 351 log.exception("Default user edit exception")
353 352 raise JSONRPCError('editing default user is forbidden')
354 353 except Exception:
355 354 log.exception("Error occurred during update of user")
356 355 raise JSONRPCError('failed to update user `%s`' % (userid,))
357 356
358 357
359 358 @jsonrpc_method()
360 359 def delete_user(request, apiuser, userid):
361 360 """
362 361 Deletes the specified user from the |RCE| user database.
363 362
364 363 This command can only be run using an |authtoken| with admin rights to
365 364 the specified repository.
366 365
367 366 .. important::
368 367
369 368 Ensure all open pull requests and open code review
370 369 requests to this user are close.
371 370
372 371 Also ensure all repositories, or repository groups owned by this
373 372 user are reassigned before deletion.
374 373
375 374 This command takes the following options:
376 375
377 376 :param apiuser: This is filled automatically from the |authtoken|.
378 377 :type apiuser: AuthUser
379 378 :param userid: Set the user to delete.
380 379 :type userid: str or int
381 380
382 381 Example output:
383 382
384 383 .. code-block:: bash
385 384
386 385 id : <id_given_in_input>
387 386 result: {
388 387 "msg" : "deleted user ID:<userid> <username>",
389 388 "user": null
390 389 }
391 390 error: null
392 391
393 392 Example error output:
394 393
395 394 .. code-block:: bash
396 395
397 396 id : <id_given_in_input>
398 397 result : null
399 398 error : {
400 399 "failed to delete user ID:<userid> <username>"
401 400 }
402 401
403 402 """
404 403 if not has_superadmin_permission(apiuser):
405 404 raise JSONRPCForbidden()
406 405
407 406 user = get_user_or_error(userid)
408 407
409 408 try:
410 409 UserModel().delete(userid)
411 410 Session().commit()
412 411 return {
413 412 'msg': 'deleted user ID:%s %s' % (user.user_id, user.username),
414 413 'user': None
415 414 }
416 415 except Exception:
417 416 log.exception("Error occurred during deleting of user")
418 417 raise JSONRPCError(
419 418 'failed to delete user ID:%s %s' % (user.user_id, user.username))
420 419
421 420
422 421 @jsonrpc_method()
423 422 def get_user_locks(request, apiuser, userid=Optional(OAttr('apiuser'))):
424 423 """
425 424 Displays all repositories locked by the specified user.
426 425
427 426 * If this command is run by a non-admin user, it returns
428 427 a list of |repos| locked by that user.
429 428
430 429 This command takes the following options:
431 430
432 431 :param apiuser: This is filled automatically from the |authtoken|.
433 432 :type apiuser: AuthUser
434 433 :param userid: Sets the userid whose list of locked |repos| will be
435 434 displayed.
436 435 :type userid: Optional(str or int)
437 436
438 437 Example output:
439 438
440 439 .. code-block:: bash
441 440
442 441 id : <id_given_in_input>
443 442 result : {
444 443 [repo_object, repo_object,...]
445 444 }
446 445 error : null
447 446 """
448 447
449 448 include_secrets = False
450 449 if not has_superadmin_permission(apiuser):
451 450 # make sure normal user does not pass someone else userid,
452 451 # he is not allowed to do that
453 452 if not isinstance(userid, Optional) and userid != apiuser.user_id:
454 453 raise JSONRPCError('userid is not the same as your user')
455 454 else:
456 455 include_secrets = True
457 456
458 457 userid = Optional.extract(userid, evaluate_locals=locals())
459 458 userid = getattr(userid, 'user_id', userid)
460 459 user = get_user_or_error(userid)
461 460
462 461 ret = []
463 462
464 463 # show all locks
465 464 for r in Repository.getAll():
466 465 _user_id, _time, _reason = r.locked
467 466 if _user_id and _time:
468 467 _api_data = r.get_api_data(include_secrets=include_secrets)
469 468 # if we use user filter just show the locks for this user
470 469 if safe_int(_user_id) == user.user_id:
471 470 ret.append(_api_data)
472 471
473 472 return ret
473
474
475 @jsonrpc_method()
476 def get_user_audit_logs(request, apiuser, userid=Optional(OAttr('apiuser'))):
477 """
478 Fetches all action logs made by the specified user.
479
480 This command takes the following options:
481
482 :param apiuser: This is filled automatically from the |authtoken|.
483 :type apiuser: AuthUser
484 :param userid: Sets the userid whose list of locked |repos| will be
485 displayed.
486 :type userid: Optional(str or int)
487
488 Example output:
489
490 .. code-block:: bash
491
492 id : <id_given_in_input>
493 result : {
494 [action, action,...]
495 }
496 error : null
497 """
498
499 if not has_superadmin_permission(apiuser):
500 # make sure normal user does not pass someone else userid,
501 # he is not allowed to do that
502 if not isinstance(userid, Optional) and userid != apiuser.user_id:
503 raise JSONRPCError('userid is not the same as your user')
504
505 userid = Optional.extract(userid, evaluate_locals=locals())
506 userid = getattr(userid, 'user_id', userid)
507 user = get_user_or_error(userid)
508
509 ret = []
510
511 # show all user actions
512 for entry in UserModel().get_user_log(user, filter_term=None):
513 ret.append(entry)
514 return ret
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now