GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#23)
by
unknown
01:10
created

abberdAPIClient._validate_and_serialize_arguments()   B

Complexity

Conditions 5

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 5
dl 0
loc 25
rs 8.0894

1 Method

Rating   Name   Duplication   Size   Complexity  
A EjabberdAPIClient.kick_user() 0 11 1
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals, print_function
3
import copy
4
5
from builtins import range
6
7
from .compat import urlparse
8
from .compat import xmlrpc_client
9
10
from . import contract, definitions, defaults
11
from .core.definitions import API, APIArgument
12
from .core.errors import IllegalArgumentError
13
14
15
class EjabberdAPIClient(contract.EjabberdAPIContract):
16
    """
17
    Python Client for the Ejabberd XML-RPC API
18
    """
19
    def __init__(self, host, port, username, password, user_domain, protocol=None, verbose=False):
20
        """
21
        Constructor
22
        :param host:
23
        :type host: str|unicode
24
        :param port:
25
        :type port: int
26
        :param username:
27
        :type username: str|unicode
28
        :param password:
29
        :type password: str|unicode
30
        :param user_domain:
31
        :type user_domain: str|unicode
32
        :param protocol: http or https
33
        :type protocol: str|unicode
34
        :param verbose:
35
        :type verbose: bool
36
        """
37
        self.host = host
38
        self.port = port
39
        self.username = username
40
        self.password = password
41
        self.user_domain = user_domain
42
        self.protocol = protocol or defaults.XMLRPC_API_PROTOCOL
43
        self.verbose = verbose
44
        self._proxy = None
45
46
    @staticmethod
47
    def get_instance(service_url, verbose=False):
48
        """
49
        Returns a EjabberdAPIClient instance based on a '12factor app' compliant service_url
50
51
        :param service_url: A connection string in the format:
52
            <http|https>://<username>:<password>@<host>(:port)/user_domain
53
        :type service_url: str|unicode
54
        :param verbose:
55
        :type verbose: bool
56
        :return: EjabberdAPIClient instance
57
        """
58
        fmt_error = \
59
            'from_string expects service_url like https://USERNAME:PASSWORD@HOST:PORT/DOMAIN'
60
61
        o = urlparse(service_url)
62
63
        protocol = o.scheme
64
        assert protocol in ('http', 'https'), fmt_error
65
66
        netloc_parts = o.netloc.split('@')
67
        assert len(netloc_parts) == 2, fmt_error
68
69
        auth, server = netloc_parts
70
71
        auth_parts = auth.split(':')
72
        assert len(auth_parts) == 2, fmt_error
73
74
        username, password = auth_parts
75
76
        server_parts = server.split(':')
77
        assert len(server_parts) <= 2, fmt_error
78
79
        if len(server_parts) == 2:
80
            host, port = server_parts
81
            port = int(port)
82
        else:
83
            host, port = server_parts[0], defaults.XMLRPC_API_PORT
84
85
        path_parts = o.path.lstrip('/').split('/')
86
        assert len(path_parts) == 1, fmt_error
87
88
        user_domain = path_parts[0]
89
90
        return EjabberdAPIClient(host, port, username, password, user_domain, protocol=protocol,
91
                                 verbose=verbose)
92
93
    @property
94
    def service_url(self):
95
        """
96
        Returns the FQDN to the Ejabberd server's XML-RPC endpoint
97
        :return:
98
        """
99
        return "%s://%s:%s/" % (self.protocol, self.host, self.port)
100
101
    @property
102
    def proxy(self):
103
        """
104
        Returns the proxy object that is used to perform the calls to the XML-RPC endpoint
105
        :rtype: :py:class:xmlrpclib.ServerProxy
106
        :return the proxy object that is used to perform the calls to the XML-RPC endpoint
107
        """
108
        if self._proxy is None:
109
            self._proxy = xmlrpc_client.ServerProxy(self.service_url, verbose=(1 if self.verbose else 0))
110
        return self._proxy
111
112
    @property
113
    def auth(self):
114
        """
115
        Returns a dictionary containing the basic authorization info
116
        :rtype: dict
117
        :return: a dictionary containing the basic authorization info
118
        """
119
        return {
120
            'user': self.username,
121
            'server': self.user_domain,
122
            'password': self.password,
123
            'admin': True
124
        }
125
126
    def echo(self, sentence):
127
        """
128
        Echo's the input back
129
        :param sentence:
130
        :type sentence: str|unicode
131
        :rtype: str|unicode
132
        :return: The echoed response, which should be the same as the input
133
        """
134
        return self._call_api(definitions.Echo, sentence=sentence)
135
136
    def registered_users(self, host):
137
        """
138
        List all registered users in the xmpp_host
139
        :param host: The XMPP_DOMAIN
140
        :type host: str|unicode
141
        :rtype: Iterable
142
        :return: A list of registered users in the xmpp_host
143
        """
144
        return self._call_api(definitions.RegisteredUsers, host=host)
145
146
    def register(self, user, host, password):
147
        """
148
        Registers a user to the ejabberd server
149
        :param user: The username for the new user
150
        :type user: str|unicode
151
        :param host: The XMPP_DOMAIN
152
        :type host: str|unicode
153
        :param password: The password for the new user
154
        :type password: str|unicode
155
        :rtype: bool
156
        :return: A boolean indicating if the registration has succeeded
157
        """
158
        return self._call_api(definitions.Register, user=user, host=host, password=password)
159
160
    def unregister(self, user, host):
161
        """
162
        UnRegisters a user from the ejabberd server
163
        :param user: The username for the new user
164
        :type user: str|unicode
165
        :param host: The XMPP_DOMAIN
166
        :type host: str|unicode
167
        :rtype: bool
168
        :return: A boolean indicating if the unregistration has succeeded
169
        """
170
        return self._call_api(definitions.UnRegister, user=user, host=host)
171
172
    def change_password(self, user, host, newpass):
173
        """
174
        Change the password for a given user
175
        :param user: The username for the user we want to change the password for
176
        :type user: str|unicode
177
        :param host: The XMPP_DOMAIN
178
        :type host: str|unicode
179
        :param newpass: The new password
180
        :type newpass: str|unicode
181
        :rtype: bool
182
        :return: A boolean indicating if the password change has succeeded
183
        """
184
        return self._call_api(definitions.ChangePassword, user=user, host=host, newpass=newpass)
185
186
    def check_password_hash(self, user, host, password):
187
        """
188
        Checks whether a password is correct for a given user. The used hash-method is fixed to sha1.
189
        :param user: The username for the user we want to check the password for
190
        :type user: str|unicode
191
        :param host: The XMPP_DOMAIN
192
        :type host: str|unicode
193
        :param password: The password we want to check for the user
194
        :type password: str|unicode
195
        :rtype: bool
196
        :return: A boolean indicating if the given password matches the user's password
197
        """
198
        return self._call_api(definitions.CheckPasswordHash, user=user, host=host, password=password)
199
200
    def set_nickname(self, user, host, nickname):
201
        """
202
        Set nickname in a user's vCard
203
        :param user: The username for the user we want to set the nickname to
204
        :type user: str|unicode
205
        :param host: The XMPP_DOMAIN
206
        :type host: str|unicode
207
        :param nickname: The nickname to assign to the user
208
        :type nickname: str|unicode
209
        :rtype: bool
210
        :return: A boolean indicating nickname was assigned successfully
211
        """
212
        return self._call_api(definitions.SetNickname, user=user, host=host, nickname=nickname)
213
214
    def connected_users(self):
215
        """
216
        List all established sessions
217
        :rtype: list
218
        :return: a list of dictionaries containing user jids
219
        """
220
        return self._call_api(definitions.ConnectedUsers)
221
222
    def connected_users_info(self):
223
        """
224
        List all established sessions and their information
225
        :rtype: list
226
        :return: a list of dictionaries containing user info
227
        """
228
        return self._call_api(definitions.ConnectedUsersInfo)
229
230
    def connected_users_number(self):
231
        """
232
        Get the number of established sessions
233
        :rtype: int
234
        :return: number of established user sessions
235
        """
236
        return self._call_api(definitions.ConnectedUsersNumber)
237
238
    def user_sessions_info(self, user, host):
239
        """
240
        Get information about all sessions of a user
241
        :param user: The username for the user we want info for
242
        :type user: str|unicode
243
        :param host: The XMPP_DOMAIN
244
        :type host: str|unicode
245
        :rtype: list
246
        :return: list of information of sessions for a user
247
        """
248
        return self._call_api(definitions.UserSessionsInfo, user=user, host=host)
249
250
    def muc_online_rooms(self, host=None):
251
        """
252
        List existing rooms ('global' to get all vhosts)
253
        :param host: The XMPP_DOMAIN
254
        :type host: str|unicode
255
        :rtype: Iterable
256
        :return: A list of online rooms in the format 'name@service'
257
        """
258
        host = host or 'global'
259
        return self._call_api(definitions.MucOnlineRooms, host=host)
260
261
    def create_room(self, name, service, host):
262
        """
263
        Create a MUC room name@service in host
264
        :param name: The name for the room
265
        :type name: str|unicode
266
        :param service: The MUC service name (e.g. "conference")
267
        :type service: str|unicode
268
        :param host: The XMPP_DOMAIN
269
        :type host: str|unicode
270
        :rtype: bool
271
        :return: A boolean indicating whether the room has been created successfully
272
        """
273
        return self._call_api(definitions.CreateRoom, name=name, service=service, host=host)
274
275
    def destroy_room(self, name, service):
276
        """
277
        Destroy a MUC room
278
        :param name: The name for the room
279
        :type name: str|unicode
280
        :param service: The MUC service name (e.g. "conference")
281
        :type service: str|unicode
282
        :rtype: bool
283
        :return: A boolean indicating whether the room has been destroyed successfully
284
        """
285
        return self._call_api(definitions.DestroyRoom, name=name, service=service)
286
287
    def get_room_options(self, name, service):
288
        """
289
        Get options from a MUC room
290
        :param name: The name for the room
291
        :type name: str|unicode
292
        :param service: The MUC service name (e.g. "conference")
293
        :type service: str|unicode
294
        :rtype: dict
295
        :return: A dict containing the room options
296
        """
297
        return self._call_api(definitions.GetRoomOptions, name=name, service=service)
298
299
    def change_room_option(self, name, service, option, value):
300
        """
301
        Change an option in a MUC room
302
        :param name: The name for the room
303
        :type name: str|unicode
304
        :param service: The MUC service name (e.g. "conference")
305
        :type service: str|unicode
306
        :param option: The option to change
307
        :type option: muc.enums.MUCRoomOption
308
        :param value: The new value
309
        :type value: str|unicode|int|bool
310
        :rtype: bool
311
        :return: A boolean indicating whether the room option has been changed successfully
312
        """
313
        return self._call_api(definitions.ChangeRoomOption, name=name, service=service, option=option, value=value)
314
315
    def set_room_affiliation(self, name, service, jid, affiliation):
316
        """
317
        Change an affiliation for a user in a MUC room
318
        :param name:The name for the room
319
        :type name: str|unicode
320
        :param service: The MUC service name (e.g. "conference")
321
        :type service: str|unicode
322
        :param jid: The jabber id for the user you want to change the affiliation for
323
        :type jid: str|unicode
324
        :param affiliation: The affiliation to the room
325
        :type affiliation: muc.enums.Affiliation
326
        :rtype: list
327
        :return: A list containing dictionaries containing affiliation info
328
        """
329
        return self._call_api(definitions.SetRoomAffiliation, name=name, service=service, jid=jid,
330
                              affiliation=affiliation)
331
332
    def get_room_affiliations(self, name, service):
333
        """
334
        Get the affiliations for a MUC room
335
        :param name:The name for the room
336
        :type name: str|unicode
337
        :param service: The MUC service name (e.g. "conference")
338
        :type service: str|unicode
339
        :return:
340
        """
341
        return self._call_api(definitions.GetRoomAffiliations, name=name, service=service)
342
343
    def add_rosteritem(self, localuser, localserver, user, server, nick, group, subs):
344
        """
345
        Add an item to a user's roster
346
347
        :param localuser: The username of user we are going to add a contact to
348
        :type localuser: str|unicode
349
        :param localserver: The XMPP_DOMAIN
350
        :type localserver: str|unicode
351
        :param user: The contact we are going to add to the user's roster
352
        :type user: str|unicode
353
        :param server: The XMPP_DOMAIN
354
        :type server: str|unicode
355
        :param nick: Nickname of the contact
356
        :type nick: str|unicode
357
        :param group: To what contact group the contact goes to
358
        :type group: str|unicode
359
        :param subs: The type of subscription
360
        :type subs: str|unicode
361
        :return:
362
        """
363
        return self._call_api(definitions.AddRosterItem,
364
                              localuser=localuser, localserver=localserver,
365
                              user=user, server=server,
366
                              nick=nick, group=group, subs=subs)
367
368
    def delete_rosteritem(self, localuser, localserver, user, server):
369
        """
370
        Delete an item from a user's roster
371
372
        :param localuser: The username of user we are going to delete a contact from
373
        :type localuser: str|unicode
374
        :param localserver: The XMPP_DOMAIN
375
        :type localserver: str|unicode
376
        :param user: The contact we are going to delete from the user's roster
377
        :type user: str|unicode
378
        :param server: The XMPP_DOMAIN
379
        :type server: str|unicode
380
        :return:
381
        """
382
        return self._call_api(definitions.DeleteRosterItem, localuser=localuser, localserver=localserver, user=user, server=server)
383
384
    def get_roster(self, user, server):
385
        """
386
        Get roster of a user
387
388
        :param user: The username of the user we want contact information for
389
        :type user: str|unicode
390
        :param server: The XMPP_DOMAIN
391
        :type server: str|unicode
392
        :rtype: Iterable
393
        :return: A list of user's contacts
394
        """
395
        return self._call_api(definitions.GetRoster, user=user, server=server)
396
397
    def check_account(self, user, host):
398
        """
399
        Check if an account exists or not
400
401
        :param user: The username of the user we want to check account existence for
402
        :type user: str|unicode
403
        :param host: The XMPP_DOMAIN
404
        :type host: str|unicode
405
        :return:
406
        """
407
        return self._call_api(definitions.CheckAccount, user=user, host=host)
408
409
    def kick_user(self, user, host):
410
        """
411
        Disconnect user's active sessions
412
413
        :param user: The username of the user we want to disconnect
414
        :type user: str|unicode
415
        :param host: The XMPP_DOMAIN
416
        :type host: str|unicode
417
        :return: The number of resources/session kicked out
418
        """
419
        return self._call_api(definitions.KickUser, user=user, host=host)
420
421
    def kick_session(self, user, host, resource, reason):
422
        """
423
        Disconnect user's session
424
425
        :param user: The username of the user we want to disconnect a session for
426
        :type user: str|unicode
427
        :param host: The XMPP_DOMAIN
428
        :type host: str|unicode
429
        :param resource: The resource of the session we want to disconnect
430
        :type resource: str|unicode
431
        :param reason: The reason why we want to disconnect the session
432
        :type reason: str|unicode
433
        :return:
434
        """
435
        return self._call_api(definitions.KickSession, user=user, host=host, resource=resource, reason=reason)
436
437
    def _validate_and_serialize_arguments(self, api, arguments):
438
        """
439
        Internal method to validate and serialize arguments
440
        :param api: An instance of an API class
441
        :param arguments: A dictionary of arguments that will be passed to the method
442
        :type arguments: dict
443
        :rtype: dict
444
        :return: The serialized arguments
445
        """
446
        serialized_arguments = {}
447
448
        for i in range(len(api.arguments)):
449
            argument_descriptor = api.arguments[i]
450
            assert isinstance(argument_descriptor, APIArgument)
451
452
            # Validate argument presence
453
            argument_name = str(argument_descriptor.name)
454
            if argument_descriptor.required and argument_name not in arguments:
455
                raise IllegalArgumentError('Missing required argument "%s"' % argument_name)
456
457
            # Serializer argument value
458
            serialized_arguments[argument_descriptor.name] = \
459
                argument_descriptor.serializer_class().to_api(arguments.get(argument_name))
460
461
        return serialized_arguments
462
463
    def _report_method_call(self, method, arguments):
464
        """
465
        Internal method to print info about a method call
466
        :param method: The name of the method to call
467
        :type method: str|unicode
468
        :param arguments: A dictionary of arguments that will be passed to the method
469
        :type: arguments: dict
470
        :return:
471
        """
472
        if self.verbose:
473
            report = '===> %s(%s)' % (method, ', '.join(['%s=%s' % (key, value) for (key, value) in arguments.items()]))
474
            print(report)
475
            return report
476
477
    def _call_api(self, api_class, **kwargs):
478
        """
479
        Internal method used to perform api calls
480
        :param api_class:
481
        :type api_class: py:class:API
482
        :param kwargs:
483
        :type kwargs: dict
484
        :rtype: object
485
        :return: Returns return value of the XMLRPC Method call
486
        """
487
        # Validate api_class
488
        assert issubclass(api_class, API)
489
490
        # Create api instance
491
        api = api_class()
492
493
        # Copy arguments
494
        arguments = copy.copy(kwargs)
495
496
        # Transform arguments
497
        arguments = api.transform_arguments(**arguments)
498
499
        # Validate and serialize arguments
500
        arguments = self._validate_and_serialize_arguments(api, arguments)
501
502
        # Retrieve method
503
        method = getattr(self.proxy, str(api.method))
504
505
        # Print method call with arguments
506
        self._report_method_call(api.method, arguments)
507
508
        # Perform call
509
        if not api.authenticate:
510
            response = method(arguments)
511
        else:
512
            response = method(self.auth, arguments)
513
514
        # Validate response
515
        api.validate_response(api, arguments, response)
516
517
        # Transform response
518
        result = api.transform_response(api, arguments, response)
519
520
        return result
521