1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
3
|
|
|
from django.contrib.auth import get_user_model |
4
|
|
|
from django.contrib.auth.models import Group |
5
|
|
|
from django.core.exceptions import PermissionDenied |
6
|
|
|
|
7
|
|
|
from modernrpc.core import rpc_method, REQUEST_KEY |
8
|
|
|
|
9
|
|
|
from tcms.rpc.serializer import XMLRPCSerializer |
10
|
|
|
from tcms.rpc.utils import parse_bool_value |
11
|
|
|
from tcms.rpc.decorators import permissions_required |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
User = get_user_model() # pylint: disable=invalid-name |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
__all__ = ( |
18
|
|
|
'update', |
19
|
|
|
'filter', |
20
|
|
|
'join_group', |
21
|
|
|
) |
22
|
|
|
|
23
|
|
|
|
24
|
|
|
def _get_user_dict(user): |
25
|
|
|
user_dict = XMLRPCSerializer(model=user).serialize_model() |
26
|
|
|
if 'password' in user_dict: |
27
|
|
|
del user_dict['password'] |
28
|
|
|
return user_dict |
29
|
|
|
|
30
|
|
|
|
31
|
|
|
@rpc_method(name='User.filter') |
32
|
|
|
def filter(query=None, **kwargs): # pylint: disable=redefined-builtin |
33
|
|
|
""" |
34
|
|
|
.. function:: XML-RPC User.filter(query) |
35
|
|
|
|
36
|
|
|
Search and return the resulting list of users. |
37
|
|
|
|
38
|
|
|
:param query: Field lookups for :class:`django.contrib.auth.models.User` |
39
|
|
|
:type query: dict |
40
|
|
|
:return: Serialized :class:`django.contrib.auth.models.User` object without |
41
|
|
|
the password field! |
42
|
|
|
:rtype: dict |
43
|
|
|
|
44
|
|
|
.. note:: |
45
|
|
|
|
46
|
|
|
If query is ``None`` will return the user issuing the RPC request. |
47
|
|
|
""" |
48
|
|
|
if not query: |
49
|
|
|
query = {'pk': kwargs.get(REQUEST_KEY).user.pk} |
50
|
|
|
|
51
|
|
|
if 'is_active' in query: |
52
|
|
|
query['is_active'] = parse_bool_value(query['is_active']) |
53
|
|
|
users = User.objects.filter(**query) |
54
|
|
|
|
55
|
|
|
filtered_users = [] |
56
|
|
|
for user in users: |
57
|
|
|
filtered_users.append(_get_user_dict(user)) |
58
|
|
|
|
59
|
|
|
return filtered_users |
60
|
|
|
|
61
|
|
|
|
62
|
|
|
@rpc_method(name='User.update') |
63
|
|
|
def update(user_id, values, **kwargs): |
64
|
|
|
""" |
65
|
|
|
.. function:: XML-RPC User.update(user_id, values) |
66
|
|
|
|
67
|
|
|
Updates the fields of the selected user. Can be used to update |
68
|
|
|
password as well! |
69
|
|
|
|
70
|
|
|
:param user_id: PK of user to update |
71
|
|
|
:type user_id: int |
72
|
|
|
:param values: Field values for :class:`django.contrib.auth.models.User` |
73
|
|
|
:type values: dict |
74
|
|
|
:return: Serialized :class:`django.contrib.auth.models.User` object |
75
|
|
|
:raises: PermissionDenied if missing the *auth.change_user* permission |
76
|
|
|
when updating another user or when passwords don't match. |
77
|
|
|
|
78
|
|
|
.. note:: |
79
|
|
|
|
80
|
|
|
If ``user_id`` is None will update the user issuing the RPC request. |
81
|
|
|
|
82
|
|
|
.. warning:: |
83
|
|
|
|
84
|
|
|
Changing the password for another user via RPC is not allowed! |
85
|
|
|
""" |
86
|
|
|
request = kwargs.get(REQUEST_KEY) |
87
|
|
|
if user_id: |
88
|
|
|
user_being_updated = User.objects.get(pk=user_id) |
89
|
|
|
else: |
90
|
|
|
user_being_updated = request.user |
91
|
|
|
|
92
|
|
|
editable_fields = ('first_name', 'last_name', 'email', 'password') |
93
|
|
|
can_change_user = request.user.has_perm('auth.change_user') |
94
|
|
|
|
95
|
|
|
is_updating_other = request.user != user_being_updated |
96
|
|
|
# If changing other's attributes, current user must have proper permission |
97
|
|
|
if is_updating_other and not can_change_user: |
98
|
|
|
raise PermissionDenied('Permission denied') |
99
|
|
|
|
100
|
|
|
update_fields = [] |
101
|
|
|
for field in editable_fields: |
102
|
|
|
if not values.get(field): |
103
|
|
|
continue |
104
|
|
|
|
105
|
|
|
update_fields.append(field) |
106
|
|
|
if field == 'password': |
107
|
|
|
if is_updating_other: |
108
|
|
|
raise PermissionDenied('Password updates for other users are not allowed via RPC!') |
109
|
|
|
|
110
|
|
|
old_password = values.get('old_password') |
111
|
|
|
if not old_password: |
112
|
|
|
raise PermissionDenied('Old password is required') |
113
|
|
|
|
114
|
|
|
if not user_being_updated.check_password(old_password): |
115
|
|
|
raise PermissionDenied('Password is incorrect') |
116
|
|
|
|
117
|
|
|
user_being_updated.set_password(values['password']) |
118
|
|
|
else: |
119
|
|
|
setattr(user_being_updated, field, values[field]) |
120
|
|
|
|
121
|
|
|
user_being_updated.save(update_fields=update_fields) |
122
|
|
|
return _get_user_dict(user_being_updated) |
123
|
|
|
|
124
|
|
|
|
125
|
|
|
@permissions_required('auth.change_user') |
126
|
|
|
@rpc_method(name='User.join_group') |
127
|
|
|
def join_group(username, groupname): |
128
|
|
|
""" |
129
|
|
|
.. function:: XML-RPC User.join_group(username, groupname) |
130
|
|
|
|
131
|
|
|
Add user to a group specified by name. |
132
|
|
|
|
133
|
|
|
:param username: Username to modify |
134
|
|
|
:type username: str |
135
|
|
|
:param groupname: Name of group to join, must exist! |
136
|
|
|
:type groupname: str |
137
|
|
|
:return: None |
138
|
|
|
:raises: PermissionDenied if missing *auth.change_user* permission |
139
|
|
|
""" |
140
|
|
|
user = User.objects.get(username=username) |
141
|
|
|
group = Group.objects.get(name=groupname) |
142
|
|
|
user.groups.add(group) |
143
|
|
|
|