Test Failed
Push — master ( ce197a...ce197a )
by Andreas
04:09 queued 02:49
created

TpmApi.create_project()   A

Complexity

Conditions 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 1
Metric Value
cc 1
dl 0
loc 7
rs 9.4285
c 3
b 2
f 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.4'
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
        self.max_retries = 3
105
        for key in kwargs:
106
            if key == 'private_key':
107
                self.private_key = kwargs[key]
108
            elif key == 'public_key':
109
                self.public_key = kwargs[key]
110
            elif key == 'username':
111
                self.username = kwargs[key]
112
            elif key == 'password':
113
                self.password = kwargs[key]
114
            elif key == 'max_retries':
115
                self.max_retries = kwargs[key]
116
                if self.max_retries < 1:
117
                    raise self.ConfigError('Parameter max_retires should be at least 1')
118
        log.debug("Max retries on ValueError: {}".format(self.max_retries))
119
        if self.private_key is not False and self.public_key is not False and\
120
                self.username is False and self.password is False:
121
            log.debug('Using Private/Public Key authentication.')
122
        elif self.username is not False and self.password is not False and\
123
                self.private_key is False and self.public_key is False:
124
            log.debug('Using Basic authentication.')
125
        else:
126
            raise self.ConfigError('No authentication specified'
127
                                   ' (user/password or private/public key)')
128
129
    def request(self, path, action, data=''):
130
        """To make a request to the API."""
131
        # Check if the path includes URL or not.
132
        head = self.base_url
133
        if path.startswith(head):
134
            path = path[len(head):]
135
        if not path.startswith(self.api):
136
            path = self.api + path
137
        log.debug('Using path %s' % path)
138
139
        # If we have data, convert to JSON
140
        if data:
141
            data = json.dumps(data)
142
            log.debug('Data to sent: %s' % data)
143
        # In case of key authentication
144
        if self.private_key and self.public_key:
145
            timestamp = str(int(time.time()))
146
            log.debug('Using timestamp: {}'.format(timestamp))
147
            unhashed = path + timestamp + str(data)
148
            log.debug('Using message: {}'.format(unhashed))
149
            self.hash = hmac.new(str.encode(self.private_key),
150
                                 msg=unhashed.encode('utf-8'),
151
                                 digestmod=hashlib.sha256).hexdigest()
152
            log.debug('Authenticating with hash: %s' % self.hash)
153
            self.headers['X-Public-Key'] = self.public_key
154
            self.headers['X-Request-Hash'] = self.hash
155
            self.headers['X-Request-Timestamp'] = timestamp
156
            auth = False
157
        # In case of user credentials authentication
158
        elif self.username and self.password:
159
            auth = requests.auth.HTTPBasicAuth(self.username, self.password)
160
        # Set unlock reason
161
        if self.unlock_reason:
162
            self.headers['X-Unlock-Reason'] = self.unlock_reason
163
            log.info('Unlock Reason: %s' % self.unlock_reason)
164
        url = head + path
165
        # Try API request and handle Exceptions
166
        try:
167
            if action == 'get':
168
                log.debug('GET request %s' % url)
169
                self.req = requests.get(url, headers=self.headers, auth=auth,
170
                                        verify=False)
171
            elif action == 'post':
172
                log.debug('POST request %s' % url)
173
                self.req = requests.post(url, headers=self.headers, auth=auth,
174
                                         verify=False, data=data)
175
            elif action == 'put':
176
                log.debug('PUT request %s' % url)
177
                self.req = requests.put(url, headers=self.headers,
178
                                        auth=auth, verify=False,
179
                                        data=data)
180
            elif action == 'delete':
181
                log.debug('DELETE request %s' % url)
182
                self.req = requests.delete(url, headers=self.headers,
183
                                           verify=False, auth=auth)
184
185
            if self.req.content == b'':
186
                result = None
187
                log.debug('No result returned.')
188
            else:
189
                result = self.req.json()
190
                if 'error' in result and result['error']:
191
                    raise TPMException(result['message'])
192
193
        except requests.exceptions.RequestException as e:
194
            log.critical("Connection error for " + str(e))
195
            raise TPMException("Connection error for " + str(e))
196
197
        except ValueError as e:
198
            if self.req.status_code == 403:
199
                log.warning(url + " forbidden")
200
                raise TPMException(url + " forbidden")
201
            elif self.req.status_code == 404:
202
                log.warning(url + " forbidden")
203
                raise TPMException(url + " not found")
204
            else:
205
                message = ('%s: %s %s' % (e, self.req.url, self.req.text))
206
                log.debug(message)
207
                raise ValueError(message)
208
209
        return result
210
211
    def post(self, path, data=''):
212
        """For post based requests."""
213
        return self.request(path, 'post', data)
214
215
    def get(self, path):
216
        """For get based requests."""
217
        return self.request(path, 'get')
218
219
    def put(self, path, data=''):
220
        """For put based requests."""
221
        self.request(path, 'put', data)
222
223
    def delete(self, path):
224
        """For delete based requests."""
225
        self.request(path, 'delete')
226
227
    def get_collection(self, path):
228
        """To get pagewise data."""
229
        while True:
230
            items = self.get(path)
231
            req = self.req
232
            for item in items:
233
                yield item
234
            if req.links and req.links['next'] and\
235
                    req.links['next']['rel'] == 'next':
236
                path = req.links['next']['url']
237
            else:
238
                break
239
240
    def collection(self, path):
241
        """To return all items generated by get collection."""
242
        data = []
243
        for item in self.get_collection(path):
244
            data.append(item)
245
        return data
246
247
    # From now on, Functions that work that way in all API Versions.
248
249
    # http://teampasswordmanager.com/docs/api-projects/#list_projects
250
    def list_projects(self):
251
        """List projects."""
252
        log.debug('List all projects.')
253
        return self.collection('projects.json')
254
255
    def list_projects_archived(self):
256
        """List archived projects."""
257
        log.debug('List all archived projects.')
258
        return self.collection('projects/archived.json')
259
260
    def list_projects_favorite(self):
261
        """List favorite projects."""
262
        log.debug('List all favorite projects.')
263
        return self.collection('projects/favorite.json')
264
265
    def list_projects_search(self, searchstring):
266
        """List projects with searchstring."""
267
        log.debug('List all projects with: %s' % searchstring)
268
        return self.collection('projects/search/%s.json' %
269
                               quote_plus(searchstring))
270
271
    def show_project(self, ID):
272
        """Show a project."""
273
        # http://teampasswordmanager.com/docs/api-projects/#show_project
274
        log.debug('Show project info: %s' % ID)
275
        return self.get('projects/%s.json' % ID)
276
277
    def list_passwords_of_project(self, ID):
278
        """List passwords of project."""
279
        # http://teampasswordmanager.com/docs/api-projects/#list_pwds_prj
280
        log.debug('List passwords of project: %s' % ID)
281
        return self.collection('projects/%s/passwords.json' % ID)
282
283
    def list_user_access_on_project(self, ID):
284
        """List users who can access a project."""
285
        # http://teampasswordmanager.com/docs/api-projects/#list_users_prj
286
        log.debug('List User access on project: %s' % ID)
287
        return self.collection('projects/%s/security.json' % ID)
288
289
    def create_project(self, data):
290
        """Create a project."""
291
        # http://teampasswordmanager.com/docs/api-projects/#create_project
292
        log.info('Create project: %s' % data)
293
        NewID = self.post('projects.json', data).get('id')
294
        log.info('Project has been created with ID %s' % NewID)
295
        return NewID
296
297
    def update_project(self, ID, data):
298
        """Update a project."""
299
        # http://teampasswordmanager.com/docs/api-projects/#update_project
300
        log.info('Update project %s with %s' % (ID, data))
301
        self.put('projects/%s.json' % ID, data)
302
303
    def change_parent_of_project(self, ID, NewParrentID):
304
        """Change parent of project."""
305
        # http://teampasswordmanager.com/docs/api-projects/#change_parent
306
        log.info('Change parrent for project %s to %s' % (ID, NewParrentID))
307
        data = {'parent_id': NewParrentID}
308
        self.put('projects/%s/change_parent.json' % ID, data)
309
310
    def update_security_of_project(self, ID, data):
311
        """Update security of project."""
312
        # http://teampasswordmanager.com/docs/api-projects/#update_project_security
313
        log.info('Update project %s security %s' % (ID, data))
314
        self.put('projects/%s/security.json' % ID, data)
315
316
    def archive_project(self, ID):
317
        """Archive a project."""
318
        # http://teampasswordmanager.com/docs/api-projects/#arch_unarch_project
319
        log.info('Archive project %s' % ID)
320
        self.put('projects/%s/archive.json' % ID)
321
322
    def unarchive_project(self, ID):
323
        """Un-Archive a project."""
324
        # http://teampasswordmanager.com/docs/api-projects/#arch_unarch_project
325
        log.info('Unarchive project %s' % ID)
326
        self.put('projects/%s/unarchive.json' % ID)
327
328
    def delete_project(self, ID):
329
        """Delete a project."""
330
        # http://teampasswordmanager.com/docs/api-projects/#delete_project
331
        log.info('Delete project %s' % ID)
332
        self.delete('projects/%s.json' % ID)
333
334
    # http://teampasswordmanager.com/docs/api-passwords/#list_passwords
335
    def list_passwords(self):
336
        """List passwords."""
337
        log.debug('List all passwords.')
338
        return self.collection('passwords.json')
339
340
    def list_passwords_archived(self):
341
        """List archived passwords."""
342
        log.debug('List archived passwords.')
343
        return self.collection('passwords/archived.json')
344
345
    def list_passwords_favorite(self):
346
        """List favorite passwords."""
347
        log.debug('List favorite spasswords.')
348
        return self.collection('passwords/favorite.json')
349
350
    def list_passwords_search(self, searchstring):
351
        """List passwords with searchstring."""
352
        log.debug('List all passwords with: %s' % searchstring)
353
        return self.collection('passwords/search/%s.json' %
354
                               quote_plus(searchstring))
355
356
    def show_password(self, ID):
357
        """Show password."""
358
        # http://teampasswordmanager.com/docs/api-passwords/#show_password
359
        log.info('Show password info: %s' % ID)
360
        return self.get('passwords/%s.json' % ID)
361
362
    def list_user_access_on_password(self, ID):
363
        """List users who can access a password."""
364
        # http://teampasswordmanager.com/docs/api-passwords/#list_users_pwd
365
        log.debug('List user access on password %s' % ID)
366
        return self.collection('passwords/%s/security.json' % ID)
367
368
    def create_password(self, data):
369
        """Create a password."""
370
        # http://teampasswordmanager.com/docs/api-passwords/#create_password
371
        log.info('Create new password %s' % data)
372
        NewID = self.post('passwords.json', data).get('id')
373
        log.info('Password has been created with ID %s' % NewID)
374
        return NewID
375
376
    def update_password(self, ID, data):
377
        """Update a password."""
378
        # http://teampasswordmanager.com/docs/api-passwords/#update_password
379
        log.info('Update Password %s with %s' % (ID, data))
380
        self.put('passwords/%s.json' % ID, data)
381
382
    def update_security_of_password(self, ID, data):
383
        """Update security of a password."""
384
        # http://teampasswordmanager.com/docs/api-passwords/#update_security_password
385
        log.info('Update security of password %s with %s' % (ID, data))
386
        self.put('passwords/%s/security.json' % ID, data)
387
388
    def update_custom_fields_of_password(self, ID, data):
389
        """Update custom fields definitions of a password."""
390
        # http://teampasswordmanager.com/docs/api-passwords/#update_cf_password
391
        log.info('Update custom fields of password %s with %s' % (ID, data))
392
        self.put('passwords/%s/custom_fields.json' % ID, data)
393
394
    def delete_password(self, ID):
395
        """Delete a password."""
396
        # http://teampasswordmanager.com/docs/api-passwords/#delete_password
397
        log.info('Delete password %s' % ID)
398
        self.delete('passwords/%s.json' % ID)
399
400
    def lock_password(self, ID):
401
        """Lock a password."""
402
        # http://teampasswordmanager.com/docs/api-passwords/#lock_password
403
        log.info('Lock password %s' % ID)
404
        self.put('passwords/%s/lock.json' % ID)
405
406
    def unlock_password(self, ID, reason):
407
        """Unlock a password."""
408
        # http://teampasswordmanager.com/docs/api-passwords/#unlock_password
409
        log.info('Unlock password %s, Reason: %s' % (ID, reason))
410
        self.unlock_reason = reason
411
        self.put('passwords/%s/unlock.json' % ID)
412
413
    def list_mypasswords(self):
414
        """List my passwords."""
415
        # http://teampasswordmanager.com/docs/api-my-passwords/#list_passwords
416
        log.debug('List MyPasswords')
417
        return self.collection('my_passwords.json')
418
419
    def list_mypasswords_search(self, searchstring):
420
        """List my passwords with searchstring."""
421
        # http://teampasswordmanager.com/docs/api-my-passwords/#list_passwords
422
        log.debug('List MyPasswords with %s' % searchstring)
423
        return self.collection('my_passwords/search/%s.json' %
424
                               quote_plus(searchstring))
425
426
    def show_mypassword(self, ID):
427
        """Show my password."""
428
        # http://teampasswordmanager.com/docs/api-my-passwords/#show_password
429
        log.debug('Show MyPassword %s' % ID)
430
        return self.get('my_passwords/%s.json' % ID)
431
432
    def create_mypassword(self, data):
433
        """Create my password."""
434
        # http://teampasswordmanager.com/docs/api-my-passwords/#create_password
435
        log.info('Create MyPassword with %s' % data)
436
        NewID = self.post('my_passwords.json', data).get('id')
437
        log.info('MyPassword has been created with %s' % NewID)
438
        return NewID
439
440
    def update_mypassword(self, ID, data):
441
        """Update my password."""
442
        # http://teampasswordmanager.com/docs/api-my-passwords/#update_password
443
        log.info('Update MyPassword %s with %s' % (ID, data))
444
        self.put('my_passwords/%s.json' % ID, data)
445
446
    def delete_mypassword(self, ID):
447
        """Delete my password."""
448
        # http://teampasswordmanager.com/docs/api-my-passwords/#delete_password
449
        log.info('Delete password %s' % ID)
450
        self.delete('my_passwords/%s.json' % ID)
451
452
    def set_favorite_password(self, ID):
453
        """Set a password as favorite."""
454
        # http://teampasswordmanager.com/docs/api-favorites/#set_fav
455
        log.info('Set password %s as favorite' % ID)
456
        self.post('favorite_passwords/%s.json' % ID)
457
458
    def unset_favorite_password(self, ID):
459
        """Unet a password as favorite."""
460
        # http://teampasswordmanager.com/docs/api-favorites/#del_fav
461
        log.info('Unset password %s as favorite' % ID)
462
        self.delete('favorite_passwords/%s.json' % ID)
463
464
    def set_favorite_project(self, ID):
465
        """Set a project as favorite."""
466
        # http://teampasswordmanager.com/docs/api-favorites/#set_fav
467
        log.info('Set project %s as favorite' % ID)
468
        self.post('favorite_project/%s.json' % ID)
469
470
    def unset_favorite_project(self, ID):
471
        """Unet a project as favorite."""
472
        # http://teampasswordmanager.com/docs/api-favorites/#del_fav
473
        log.info('Unset project %s as favorite' % ID)
474
        self.delete('favorite_project/%s.json' % ID)
475
476
    def list_users(self):
477
        """List users."""
478
        # http://teampasswordmanager.com/docs/api-users/#list_users
479
        log.debug('List users')
480
        return self.collection('users.json')
481
482
    def show_user(self, ID):
483
        """Show a user."""
484
        # http://teampasswordmanager.com/docs/api-users/#show_user
485
        log.debug('Show user %s' % ID)
486
        return self.get('users/%s.json' % ID)
487
488
    def show_me(self):
489
        """Show me."""
490
        # http://teampasswordmanager.com/docs/api-users/#show_me
491
        log.debug('Show Info about own user')
492
        return self.get('users/me.json')
493
494
    def who_am_i(self):
495
        """Who am I."""
496
        return self.show_me()
497
498
    def create_user(self, data):
499
        """Create a User."""
500
        # http://teampasswordmanager.com/docs/api-users/#create_user
501
        log.info('Create user with %s' % data)
502
        NewID = self.post('users.json', data).get('id')
503
        log.info('User has been created with ID %s' % NewID)
504
        return NewID
505
506
    def update_user(self, ID, data):
507
        """Update a User."""
508
        # http://teampasswordmanager.com/docs/api-users/#update_user
509
        log.info('Update user %s with %s' % (ID, data))
510
        self.put('users/%s.json' % ID, data)
511
512
    def change_user_password(self, ID, data):
513
        """Change password of a User."""
514
        # http://teampasswordmanager.com/docs/api-users/#change_password
515
        log.info('Change user %s password' % ID)
516
        self.put('users/%s/change_password.json' % ID, data)
517
518
    def activate_user(self, ID):
519
        """Activate a User."""
520
        # http://teampasswordmanager.com/docs/api-users/#activate_deactivate
521
        log.info('Activate user %s' % ID)
522
        self.put('users/%s/activate.json' % ID)
523
524
    def deactivate_user(self, ID):
525
        """Dectivate a User."""
526
        # http://teampasswordmanager.com/docs/api-users/#activate_deactivate
527
        log.info('Deactivate user %s' % ID)
528
        self.put('users/%s/deactivate.json' % ID)
529
530
    def convert_user_to_ldap(self, ID, DN):
531
        """Convert a normal user to a LDAP user."""
532
        # http://teampasswordmanager.com/docs/api-users/#convert_to_ldap
533
        data = {'login_dn': DN}
534
        log.info('Convert User %s to LDAP DN %s' % (ID, DN))
535
        self.put('users/%s/convert_to_ldap.json' % ID, data)
536
537
    def convert_ldap_user_to_normal(self, ID):
538
        """Convert a LDAP user to a normal user."""
539
        log.info('Convert User %s from LDAP to normal user' % ID)
540
        self.put('users/%s/convert_to_normal.json' % ID)
541
542
    def delete_user(self, ID):
543
        """Delete a user."""
544
        # http://teampasswordmanager.com/docs/api-users/#delete_user
545
        log.info('Delete user %s' % ID)
546
        self.delete('users/%s.json' % ID)
547
548
    def list_groups(self):
549
        """List Groups."""
550
        # http://teampasswordmanager.com/docs/api-groups/#list_groups
551
        log.debug('List groups')
552
        return self.collection('groups.json')
553
554
    def show_group(self, ID):
555
        """Show a Group."""
556
        # http://teampasswordmanager.com/docs/api-groups/#show_group
557
        log.debug('Show group %s' % ID)
558
        return self.get('groups/%s.json' % ID)
559
560
    def create_group(self, data):
561
        """Create a Group."""
562
        # http://teampasswordmanager.com/docs/api-groups/#create_group
563
        log.info('Create group with %s' % data)
564
        NewID = self.post('groups.json', data).get('id')
565
        log.info('Group has been created with ID %s' % NewID)
566
        return NewID
567
568
    def update_group(self, ID, data):
569
        """Update a Group."""
570
        # http://teampasswordmanager.com/docs/api-groups/#update_group
571
        log.info('Update group %s with %s' % (ID, data))
572
        self.put('groups/%s.json' % ID, data)
573
574
    def add_user_to_group(self, GroupID, UserID):
575
        """Add a user to a group."""
576
        # http://teampasswordmanager.com/docs/api-groups/#add_user
577
        log.info('Add User %s to Group %s' % (UserID, GroupID))
578
        self.put('groups/%s/add_user/%s.json' % (GroupID, UserID))
579
580
    def delete_user_from_group(self, GroupID, UserID):
581
        """Delete a user from a group."""
582
        # http://teampasswordmanager.com/docs/api-groups/#del_user
583
        log.info('Delete user %s from group %s' % (UserID, GroupID))
584
        self.put('groups/%s/delete_user/%s.json' % (GroupID, UserID))
585
586
    def delete_group(self, ID):
587
        """Delete a group."""
588
        # http://teampasswordmanager.com/docs/api-groups/#delete_group
589
        log.info('Delete group %s' % ID)
590
        self.delete('groups/%s.json' % ID)
591
592
    def generate_password(self):
593
        """Generate a new random password."""
594
        # http://teampasswordmanager.com/docs/api-passwords-generator/
595
        log.debug('Generate new password')
596
        return self.get('generate_password.json')
597
598
    def get_version(self):
599
        """Get Version Information."""
600
        # http://teampasswordmanager.com/docs/api-version/
601
        log.debug('Get version information')
602
        return self.get('version.json')
603
604
    def get_latest_version(self):
605
        """Check for latest version."""
606
        # http://teampasswordmanager.com/docs/api-version/
607
        log.debug('Get latest version')
608
        return self.get('version/check_latest.json')
609
610
    def up_to_date(self):
611
        """Check if Team Password Manager is up to date."""
612
        VersionInfo = self.get_latest_version()
613
        CurrentVersion = VersionInfo.get('version')
614
        LatestVersion = VersionInfo.get('latest_version')
615
        if  CurrentVersion == LatestVersion:
616
            log.info('TeamPasswordManager is up-to-date!')
617
            log.debug('Current Version: {} Latest Version: {}'.format(LatestVersion, LatestVersion))
618
            return True
619
        else:
620
            log.warning('TeamPasswordManager is not up-to-date!')
621
            log.debug('Current Version: {} Latest Version: {}'.format(LatestVersion, LatestVersion))
622
            return False
623
624
625
class TpmApiv3(TpmApi):
626
    """API v3 based class."""
627
    def __init__(self, url, **kwargs):
628
        super(TpmApiv3, self).__init__('v3', url, kwargs)
629
    """From now on, Functions that only work with API v3."""
630
631
632
class TpmApiv4(TpmApi):
633
    """API v4 based class."""
634
    def __init__(self, url, **kwargs):
635
        super(TpmApiv4, self).__init__('v4', url, kwargs)
636
    """From now on, Functions that only work with API v4."""
637
638
    def list_subprojects(self, ID):
639
        """List subprojects."""
640
        # http://teampasswordmanager.com/docs/api-projects/#list_subprojects
641
        return self.collection('projects/%s/subprojects.json' % ID)
642
643
    def list_subprojects_action(self, ID, action):
644
        """List subprojects with allowed action."""
645
        return self.collection('projects/%s/subprojects/%s.json' %
646
                               (ID, action))
647