Passed
Push — master ( ae226f...980100 )
by Alexander
03:01
created

tcms.rpc.api.user   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 143
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 17
eloc 63
dl 0
loc 143
rs 10
c 0
b 0
f 0

4 Functions

Rating   Name   Duplication   Size   Complexity  
A _get_user_dict() 0 5 2
A join_group() 0 18 1
A filter() 0 29 4
C update() 0 61 10
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