TpmApiv3   A
last analyzed

Complexity

Total Complexity 1

Size/Duplication

Total Lines 5
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 1
Metric Value
dl 0
loc 5
rs 10
c 3
b 1
f 1
wmc 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 2 1
1
#! /usr/bin/env python
2
"""Team Password Manager API
3
4
To simplify usage of Team Password Manager API.
5
6
You can authenticate with username and password
7
    >>> import tpm
8
    >>> URL = "https://mypasswordmanager.example.com"
9
    >>> USER = 'MyUser'
10
    >>> PASS = 'Secret'
11
    >>> tpmconn = tpm.TpmApiv4(URL, username=USER, password=PASS)
12
13
Or with Private/Public Key
14
    >>> pubkey = '3726d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897d579466c28b7f8ff51cd0'
15
    >>> privkey = '87324bedead51af96a45271d217b8ad5ef3f220da6c078a9bce4e4318729189c'
16
    >>> tpmconn = tpm.TpmApiv4(URL, private_key=privkey, public_key=pubkey)
17
18
With the connection object you can use all TPM functions, like list all passwords:
19
    >>> tpmconn.list_passwords()
20
21
All API functions from Team Password Manager are included.
22
see http://teampasswordmanager.com/docs/api/
23
24
:copyright: (c) 2017 by Andreas Hubert.
25
:license: The MIT License (MIT), see LICENSE for more details.
26
"""
27
28
__version__ = '3.5'
29
30
import hmac
31
import hashlib
32
import time
33
import requests
34
import re
35
import json
36
import logging
37
from future.standard_library import install_aliases
38
install_aliases()
39
40
from urllib.parse import quote_plus
41
42
# set logger
43
log = logging.getLogger(__name__)
44
# disable unsecure SSL warning
45
requests.packages.urllib3.disable_warnings()
46
47
48
class TPMException(Exception):
49
    pass
50
51
52
class TpmApi(object):
53
    """Settings needed for the connection to Team Password Manager."""
54
    class ConfigError(Exception):
55
        """To throw Exception based on wrong Settings."""
56
        def __init__(self, value):
57
            self.value = value
58
            log.critical(value)
59
60
        def __str__(self):
61
            return repr(self.value)
62
63
    def __init__(self, api, base_url, kwargs):
64
        """init thing."""
65
        # Check if API version is not bullshit
66
        REGEXurl = "^" \
67
                   "(?:(?:https?)://)" \
68
                   "(?:\\S+(?::\\S*)?@)?" \
69
                   "(?:" \
70
                   "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" \
71
                   "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" \
72
                   "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" \
73
                   "|" \
74
                   "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" \
75
                   "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" \
76
                   "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))?" \
77
                   ".?" \
78
                   ")" \
79
                   "(?::\\d{2,5})?" \
80
                   "(?:[/?#]\\S*)?" \
81
                   "$"
82
        self.apiurl = 'api/' + api + '/'
83
        log.debug('Set as apiurl: %s' % self.apiurl)
84
        self.api = self.apiurl
85
        # Check if URL is not bullshit
86
        if re.match(REGEXurl, base_url):
87
            self.base_url = base_url + '/index.php/'
88
            log.debug('Set Base URL to %s' % self.base_url)
89
            self.url = self.base_url + self.apiurl
90
            log.debug('Set URL to %s' % self.url)
91
        else:
92
            raise self.ConfigError('Invalid URL: %s' % base_url)
93
        # set headers
94
        self.headers = {'Content-Type': 'application/json; charset=utf-8',
95
                        'User-Agent': 'tpm.py/' + __version__
96
                        }
97
        log.debug('Set header to %s' % self.headers)
98
        # check kwargs for either keys or user credentials
99
        self.private_key = False
100
        self.public_key = False
101
        self.username = False
102
        self.password = False
103
        self.unlock_reason = False
104
        for key in kwargs:
105
            if key == 'private_key':
106
                self.private_key = kwargs[key]
107
            elif key == 'public_key':
108
                self.public_key = kwargs[key]
109
            elif key == 'username':
110
                self.username = kwargs[key]
111
            elif key == 'password':
112
                self.password = kwargs[key]
113
            elif key == 'unlock_reason':
114
                self.unlock_reason = kwargs[key]
115
        if self.private_key is not False and self.public_key is not False and\
116
                self.username is False and self.password is False:
117
            log.debug('Using Private/Public Key authentication.')
118
        elif self.username is not False and self.password is not False and\
119
                self.private_key is False and self.public_key is False:
120
            log.debug('Using Basic authentication.')
121
        else:
122
            raise self.ConfigError('No authentication specified'
123
                                   ' (user/password or private/public key)')
124
            
125
126
    def request(self, path, action, data=''):
127
        """To make a request to the API."""   
128
        # Check if the path includes URL or not.
129
        head = self.base_url
130
        if path.startswith(head):
131
            path = path[len(head):]
132
        if not path.startswith(self.api):
133
            path = self.api + path
134
        log.debug('Using path %s' % path)                
135
136
        # If we have data, convert to JSON
137
        if data:
138
            data = json.dumps(data)
139
            log.debug('Data to sent: %s' % data)
140
        # In case of key authentication
141
        if self.private_key and self.public_key:
142
            timestamp = str(int(time.time()))
143
            log.debug('Using timestamp: {}'.format(timestamp))
144
            unhashed = path + timestamp + str(data)
145
            log.debug('Using message: {}'.format(unhashed))
146
            self.hash = hmac.new(str.encode(self.private_key),
147
                                 msg=unhashed.encode('utf-8'),
148
                                 digestmod=hashlib.sha256).hexdigest()
149
            log.debug('Authenticating with hash: %s' % self.hash)
150
            self.headers['X-Public-Key'] = self.public_key
151
            self.headers['X-Request-Hash'] = self.hash
152
            self.headers['X-Request-Timestamp'] = timestamp
153
            auth = False
154
        # In case of user credentials authentication
155
        elif self.username and self.password:
156
            auth = requests.auth.HTTPBasicAuth(self.username, self.password)
157
        # Set unlock reason
158
        if self.unlock_reason:
159
            self.headers['X-Unlock-Reason'] = self.unlock_reason
160
            log.info('Unlock Reason: %s' % self.unlock_reason)
161
        url = head + path
162
        # Try API request and handle Exceptions
163
        try:
164
            if action == 'get':
165
                log.debug('GET request %s' % url)
166
                self.req = requests.get(url, headers=self.headers, auth=auth,
167
                                        verify=False)
168
            elif action == 'post':
169
                log.debug('POST request %s' % url)
170
                self.req = requests.post(url, headers=self.headers, auth=auth,
171
                                         verify=False, data=data)
172
            elif action == 'put':
173
                log.debug('PUT request %s' % url)
174
                self.req = requests.put(url, headers=self.headers,
175
                                        auth=auth, verify=False,
176
                                        data=data)
177
            elif action == 'delete':
178
                log.debug('DELETE request %s' % url)
179
                self.req = requests.delete(url, headers=self.headers,
180
                                           verify=False, auth=auth)
181
182
            if self.req.content == b'':
183
                result = None
184
                log.debug('No result returned.')
185
            else:
186
                result = self.req.json()
187
                if 'error' in result and result['error']:
188
                    raise TPMException(result['message'])
189
190
        except requests.exceptions.RequestException as e:
191
            log.critical("Connection error for " + str(e))
192
            raise TPMException("Connection error for " + str(e))
193
194
        except ValueError as e:
195
            if self.req.status_code == 403:
196
                log.warning(url + " forbidden")
197
                raise TPMException(url + " forbidden")
198
            elif self.req.status_code == 404:
199
                log.warning(url + " forbidden")
200
                raise TPMException(url + " not found")
201
            else:
202
                message = ('%s: %s %s' % (e, self.req.url, self.req.text))
203
                log.debug(message)
204
                raise ValueError(message)
205
206
        return result
207
208
    def post(self, path, data=''):
209
        """For post based requests."""
210
        return self.request(path, 'post', data)
211
212
    def get(self, path):
213
        """For get based requests."""
214
        return self.request(path, 'get')
215
216
    def put(self, path, data=''):
217
        """For put based requests."""
218
        self.request(path, 'put', data)
219
220
    def delete(self, path):
221
        """For delete based requests."""
222
        self.request(path, 'delete')
223
224
    def get_collection(self, path):
225
        """To get pagewise data."""
226
        while True:
227
            items = self.get(path)
228
            req = self.req
229
            for item in items:
230
                yield item
231
            if req.links and req.links['next'] and\
232
                    req.links['next']['rel'] == 'next':
233
                path = req.links['next']['url']
234
            else:
235
                break
236
237
    def collection(self, path):
238
        """To return all items generated by get collection."""
239
        data = []
240
        for item in self.get_collection(path):
241
            data.append(item)
242
        return data
243
244
    # From now on, Functions that work that way in all API Versions.
245
246
    # http://teampasswordmanager.com/docs/api-projects/#list_projects
247
    def list_projects(self):
248
        """List projects."""
249
        log.debug('List all projects.')
250
        return self.collection('projects.json')
251
252
    def list_projects_archived(self):
253
        """List archived projects."""
254
        log.debug('List all archived projects.')
255
        return self.collection('projects/archived.json')
256
257
    def list_projects_favorite(self):
258
        """List favorite projects."""
259
        log.debug('List all favorite projects.')
260
        return self.collection('projects/favorite.json')
261
262
    def list_projects_search(self, searchstring):
263
        """List projects with searchstring."""
264
        log.debug('List all projects with: %s' % searchstring)
265
        return self.collection('projects/search/%s.json' %
266
                               quote_plus(searchstring))
267
268
    def show_project(self, ID):
269
        """Show a project."""
270
        # http://teampasswordmanager.com/docs/api-projects/#show_project
271
        log.debug('Show project info: %s' % ID)
272
        return self.get('projects/%s.json' % ID)
273
274
    def list_passwords_of_project(self, ID):
275
        """List passwords of project."""
276
        # http://teampasswordmanager.com/docs/api-projects/#list_pwds_prj
277
        log.debug('List passwords of project: %s' % ID)
278
        return self.collection('projects/%s/passwords.json' % ID)
279
280
    def list_user_access_on_project(self, ID):
281
        """List users who can access a project."""
282
        # http://teampasswordmanager.com/docs/api-projects/#list_users_prj
283
        log.debug('List User access on project: %s' % ID)
284
        return self.collection('projects/%s/security.json' % ID)
285
286
    def create_project(self, data):
287
        """Create a project."""
288
        # http://teampasswordmanager.com/docs/api-projects/#create_project
289
        log.info('Create project: %s' % data)
290
        NewID = self.post('projects.json', data).get('id')
291
        log.info('Project has been created with ID %s' % NewID)
292
        return NewID
293
294
    def update_project(self, ID, data):
295
        """Update a project."""
296
        # http://teampasswordmanager.com/docs/api-projects/#update_project
297
        log.info('Update project %s with %s' % (ID, data))
298
        self.put('projects/%s.json' % ID, data)
299
300
    def change_parent_of_project(self, ID, NewParrentID):
301
        """Change parent of project."""
302
        # http://teampasswordmanager.com/docs/api-projects/#change_parent
303
        log.info('Change parrent for project %s to %s' % (ID, NewParrentID))
304
        data = {'parent_id': NewParrentID}
305
        self.put('projects/%s/change_parent.json' % ID, data)
306
307
    def update_security_of_project(self, ID, data):
308
        """Update security of project."""
309
        # http://teampasswordmanager.com/docs/api-projects/#update_project_security
310
        log.info('Update project %s security %s' % (ID, data))
311
        self.put('projects/%s/security.json' % ID, data)
312
313
    def archive_project(self, ID):
314
        """Archive a project."""
315
        # http://teampasswordmanager.com/docs/api-projects/#arch_unarch_project
316
        log.info('Archive project %s' % ID)
317
        self.put('projects/%s/archive.json' % ID)
318
319
    def unarchive_project(self, ID):
320
        """Un-Archive a project."""
321
        # http://teampasswordmanager.com/docs/api-projects/#arch_unarch_project
322
        log.info('Unarchive project %s' % ID)
323
        self.put('projects/%s/unarchive.json' % ID)
324
325
    def delete_project(self, ID):
326
        """Delete a project."""
327
        # http://teampasswordmanager.com/docs/api-projects/#delete_project
328
        log.info('Delete project %s' % ID)
329
        self.delete('projects/%s.json' % ID)
330
331
    # http://teampasswordmanager.com/docs/api-passwords/#list_passwords
332
    def list_passwords(self):
333
        """List passwords."""
334
        log.debug('List all passwords.')
335
        return self.collection('passwords.json')
336
337
    def list_passwords_archived(self):
338
        """List archived passwords."""
339
        log.debug('List archived passwords.')
340
        return self.collection('passwords/archived.json')
341
342
    def list_passwords_favorite(self):
343
        """List favorite passwords."""
344
        log.debug('List favorite spasswords.')
345
        return self.collection('passwords/favorite.json')
346
347
    def list_passwords_search(self, searchstring):
348
        """List passwords with searchstring."""
349
        log.debug('List all passwords with: %s' % searchstring)
350
        return self.collection('passwords/search/%s.json' %
351
                               quote_plus(searchstring))
352
353
    def show_password(self, ID):
354
        """Show password."""
355
        # http://teampasswordmanager.com/docs/api-passwords/#show_password
356
        log.info('Show password info: %s' % ID)
357
        return self.get('passwords/%s.json' % ID)
358
359
    def list_user_access_on_password(self, ID):
360
        """List users who can access a password."""
361
        # http://teampasswordmanager.com/docs/api-passwords/#list_users_pwd
362
        log.debug('List user access on password %s' % ID)
363
        return self.collection('passwords/%s/security.json' % ID)
364
365
    def create_password(self, data):
366
        """Create a password."""
367
        # http://teampasswordmanager.com/docs/api-passwords/#create_password
368
        log.info('Create new password %s' % data)
369
        NewID = self.post('passwords.json', data).get('id')
370
        log.info('Password has been created with ID %s' % NewID)
371
        return NewID
372
373
    def update_password(self, ID, data):
374
        """Update a password."""
375
        # http://teampasswordmanager.com/docs/api-passwords/#update_password
376
        log.info('Update Password %s with %s' % (ID, data))
377
        self.put('passwords/%s.json' % ID, data)
378
379
    def update_security_of_password(self, ID, data):
380
        """Update security of a password."""
381
        # http://teampasswordmanager.com/docs/api-passwords/#update_security_password
382
        log.info('Update security of password %s with %s' % (ID, data))
383
        self.put('passwords/%s/security.json' % ID, data)
384
385
    def update_custom_fields_of_password(self, ID, data):
386
        """Update custom fields definitions of a password."""
387
        # http://teampasswordmanager.com/docs/api-passwords/#update_cf_password
388
        log.info('Update custom fields of password %s with %s' % (ID, data))
389
        self.put('passwords/%s/custom_fields.json' % ID, data)
390
391
    def delete_password(self, ID):
392
        """Delete a password."""
393
        # http://teampasswordmanager.com/docs/api-passwords/#delete_password
394
        log.info('Delete password %s' % ID)
395
        self.delete('passwords/%s.json' % ID)
396
397
    def lock_password(self, ID):
398
        """Lock a password."""
399
        # http://teampasswordmanager.com/docs/api-passwords/#lock_password
400
        log.info('Lock password %s' % ID)
401
        self.put('passwords/%s/lock.json' % ID)
402
403
    def unlock_password(self, ID, reason):
404
        """Unlock a password."""
405
        # http://teampasswordmanager.com/docs/api-passwords/#unlock_password
406
        log.info('Unlock password %s, Reason: %s' % (ID, reason))
407
        self.unlock_reason = reason
408
        self.put('passwords/%s/unlock.json' % ID)
409
410
    def list_mypasswords(self):
411
        """List my passwords."""
412
        # http://teampasswordmanager.com/docs/api-my-passwords/#list_passwords
413
        log.debug('List MyPasswords')
414
        return self.collection('my_passwords.json')
415
416
    def list_mypasswords_search(self, searchstring):
417
        """List my passwords with searchstring."""
418
        # http://teampasswordmanager.com/docs/api-my-passwords/#list_passwords
419
        log.debug('List MyPasswords with %s' % searchstring)
420
        return self.collection('my_passwords/search/%s.json' %
421
                               quote_plus(searchstring))
422
423
    def show_mypassword(self, ID):
424
        """Show my password."""
425
        # http://teampasswordmanager.com/docs/api-my-passwords/#show_password
426
        log.debug('Show MyPassword %s' % ID)
427
        return self.get('my_passwords/%s.json' % ID)
428
429
    def create_mypassword(self, data):
430
        """Create my password."""
431
        # http://teampasswordmanager.com/docs/api-my-passwords/#create_password
432
        log.info('Create MyPassword with %s' % data)
433
        NewID = self.post('my_passwords.json', data).get('id')
434
        log.info('MyPassword has been created with %s' % NewID)
435
        return NewID
436
437
    def update_mypassword(self, ID, data):
438
        """Update my password."""
439
        # http://teampasswordmanager.com/docs/api-my-passwords/#update_password
440
        log.info('Update MyPassword %s with %s' % (ID, data))
441
        self.put('my_passwords/%s.json' % ID, data)
442
443
    def delete_mypassword(self, ID):
444
        """Delete my password."""
445
        # http://teampasswordmanager.com/docs/api-my-passwords/#delete_password
446
        log.info('Delete password %s' % ID)
447
        self.delete('my_passwords/%s.json' % ID)
448
449
    def set_favorite_password(self, ID):
450
        """Set a password as favorite."""
451
        # http://teampasswordmanager.com/docs/api-favorites/#set_fav
452
        log.info('Set password %s as favorite' % ID)
453
        self.post('favorite_passwords/%s.json' % ID)
454
455
    def unset_favorite_password(self, ID):
456
        """Unet a password as favorite."""
457
        # http://teampasswordmanager.com/docs/api-favorites/#del_fav
458
        log.info('Unset password %s as favorite' % ID)
459
        self.delete('favorite_passwords/%s.json' % ID)
460
461
    def set_favorite_project(self, ID):
462
        """Set a project as favorite."""
463
        # http://teampasswordmanager.com/docs/api-favorites/#set_fav
464
        log.info('Set project %s as favorite' % ID)
465
        self.post('favorite_project/%s.json' % ID)
466
467
    def unset_favorite_project(self, ID):
468
        """Unet a project as favorite."""
469
        # http://teampasswordmanager.com/docs/api-favorites/#del_fav
470
        log.info('Unset project %s as favorite' % ID)
471
        self.delete('favorite_project/%s.json' % ID)
472
473
    def list_users(self):
474
        """List users."""
475
        # http://teampasswordmanager.com/docs/api-users/#list_users
476
        log.debug('List users')
477
        return self.collection('users.json')
478
479
    def show_user(self, ID):
480
        """Show a user."""
481
        # http://teampasswordmanager.com/docs/api-users/#show_user
482
        log.debug('Show user %s' % ID)
483
        return self.get('users/%s.json' % ID)
484
485
    def show_me(self):
486
        """Show me."""
487
        # http://teampasswordmanager.com/docs/api-users/#show_me
488
        log.debug('Show Info about own user')
489
        return self.get('users/me.json')
490
491
    def who_am_i(self):
492
        """Who am I."""
493
        return self.show_me()
494
495
    def create_user(self, data):
496
        """Create a User."""
497
        # http://teampasswordmanager.com/docs/api-users/#create_user
498
        log.info('Create user with %s' % data)
499
        NewID = self.post('users.json', data).get('id')
500
        log.info('User has been created with ID %s' % NewID)
501
        return NewID
502
503
    def update_user(self, ID, data):
504
        """Update a User."""
505
        # http://teampasswordmanager.com/docs/api-users/#update_user
506
        log.info('Update user %s with %s' % (ID, data))
507
        self.put('users/%s.json' % ID, data)
508
509
    def change_user_password(self, ID, data):
510
        """Change password of a User."""
511
        # http://teampasswordmanager.com/docs/api-users/#change_password
512
        log.info('Change user %s password' % ID)
513
        self.put('users/%s/change_password.json' % ID, data)
514
515
    def activate_user(self, ID):
516
        """Activate a User."""
517
        # http://teampasswordmanager.com/docs/api-users/#activate_deactivate
518
        log.info('Activate user %s' % ID)
519
        self.put('users/%s/activate.json' % ID)
520
521
    def deactivate_user(self, ID):
522
        """Dectivate a User."""
523
        # http://teampasswordmanager.com/docs/api-users/#activate_deactivate
524
        log.info('Deactivate user %s' % ID)
525
        self.put('users/%s/deactivate.json' % ID)
526
527
    def convert_user_to_ldap(self, ID, DN):
528
        """Convert a normal user to a LDAP user."""
529
        # http://teampasswordmanager.com/docs/api-users/#convert_to_ldap
530
        data = {'login_dn': DN}
531
        log.info('Convert User %s to LDAP DN %s' % (ID, DN))
532
        self.put('users/%s/convert_to_ldap.json' % ID, data)
533
534
    def convert_ldap_user_to_normal(self, ID):
535
        """Convert a LDAP user to a normal user."""
536
        log.info('Convert User %s from LDAP to normal user' % ID)
537
        self.put('users/%s/convert_to_normal.json' % ID)
538
539
    def delete_user(self, ID):
540
        """Delete a user."""
541
        # http://teampasswordmanager.com/docs/api-users/#delete_user
542
        log.info('Delete user %s' % ID)
543
        self.delete('users/%s.json' % ID)
544
545
    def list_groups(self):
546
        """List Groups."""
547
        # http://teampasswordmanager.com/docs/api-groups/#list_groups
548
        log.debug('List groups')
549
        return self.collection('groups.json')
550
551
    def show_group(self, ID):
552
        """Show a Group."""
553
        # http://teampasswordmanager.com/docs/api-groups/#show_group
554
        log.debug('Show group %s' % ID)
555
        return self.get('groups/%s.json' % ID)
556
557
    def create_group(self, data):
558
        """Create a Group."""
559
        # http://teampasswordmanager.com/docs/api-groups/#create_group
560
        log.info('Create group with %s' % data)
561
        NewID = self.post('groups.json', data).get('id')
562
        log.info('Group has been created with ID %s' % NewID)
563
        return NewID
564
565
    def update_group(self, ID, data):
566
        """Update a Group."""
567
        # http://teampasswordmanager.com/docs/api-groups/#update_group
568
        log.info('Update group %s with %s' % (ID, data))
569
        self.put('groups/%s.json' % ID, data)
570
571
    def add_user_to_group(self, GroupID, UserID):
572
        """Add a user to a group."""
573
        # http://teampasswordmanager.com/docs/api-groups/#add_user
574
        log.info('Add User %s to Group %s' % (UserID, GroupID))
575
        self.put('groups/%s/add_user/%s.json' % (GroupID, UserID))
576
577
    def delete_user_from_group(self, GroupID, UserID):
578
        """Delete a user from a group."""
579
        # http://teampasswordmanager.com/docs/api-groups/#del_user
580
        log.info('Delete user %s from group %s' % (UserID, GroupID))
581
        self.put('groups/%s/delete_user/%s.json' % (GroupID, UserID))
582
583
    def delete_group(self, ID):
584
        """Delete a group."""
585
        # http://teampasswordmanager.com/docs/api-groups/#delete_group
586
        log.info('Delete group %s' % ID)
587
        self.delete('groups/%s.json' % ID)
588
589
    def generate_password(self):
590
        """Generate a new random password."""
591
        # http://teampasswordmanager.com/docs/api-passwords-generator/
592
        log.debug('Generate new password')
593
        return self.get('generate_password.json')
594
595
    def get_version(self):
596
        """Get Version Information."""
597
        # http://teampasswordmanager.com/docs/api-version/
598
        log.debug('Get version information')
599
        return self.get('version.json')
600
601
    def get_latest_version(self):
602
        """Check for latest version."""
603
        # http://teampasswordmanager.com/docs/api-version/
604
        log.debug('Get latest version')
605
        return self.get('version/check_latest.json')
606
607
    def up_to_date(self):
608
        """Check if Team Password Manager is up to date."""
609
        VersionInfo = self.get_latest_version()
610
        CurrentVersion = VersionInfo.get('version')
611
        LatestVersion = VersionInfo.get('latest_version')
612
        if  CurrentVersion == LatestVersion:
613
            log.info('TeamPasswordManager is up-to-date!')
614
            log.debug('Current Version: {} Latest Version: {}'.format(LatestVersion, LatestVersion))
615
            return True
616
        else:
617
            log.warning('TeamPasswordManager is not up-to-date!')
618
            log.debug('Current Version: {} Latest Version: {}'.format(LatestVersion, LatestVersion))
619
            return False
620
621
622
class TpmApiv3(TpmApi):
623
    """API v3 based class."""
624
    def __init__(self, url, **kwargs):
625
        super(TpmApiv3, self).__init__('v3', url, kwargs)
626
    """From now on, Functions that only work with API v3."""
627
628
629
class TpmApiv4(TpmApi):
630
    """API v4 based class."""
631
    def __init__(self, url, **kwargs):
632
        super(TpmApiv4, self).__init__('v4', url, kwargs)
633
    """From now on, Functions that only work with API v4."""
634
635
    def list_subprojects(self, ID):
636
        """List subprojects."""
637
        # http://teampasswordmanager.com/docs/api-projects/#list_subprojects
638
        return self.collection('projects/%s/subprojects.json' % ID)
639
640
    def list_subprojects_action(self, ID, action):
641
        """List subprojects with allowed action."""
642
        return self.collection('projects/%s/subprojects/%s.json' %
643
                               (ID, action))
644