PyOTP 1.4__tar.gz → 1.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.0
2
2
  Name: pyotp
3
- Version: 1.4
3
+ Version: 1.4.1
4
4
  Summary: https://github.com/nathforge/pyotp
5
5
  Home-page: https://github.com/nathforge/pyotp
6
6
  Author: Mark Percival (ported by Nathan Reynolds to Python)
@@ -3,7 +3,7 @@
3
3
  from distutils.core import setup
4
4
 
5
5
  setup(
6
- version='1.4',
6
+ version='1.4.1',
7
7
  url='https://github.com/nathforge/pyotp',
8
8
  name='pyotp',
9
9
  description='https://github.com/nathforge/pyotp',
@@ -1,6 +1,8 @@
1
1
  from pyotp.otp import OTP
2
2
  from pyotp.hotp import HOTP
3
3
  from pyotp.totp import TOTP
4
+ import utils
5
+
4
6
  import base64
5
7
  import random
6
8
 
@@ -1,5 +1,5 @@
1
1
  from pyotp.otp import OTP
2
- import urllib
2
+ from pyotp import utils
3
3
 
4
4
 
5
5
  class HOTP(OTP):
@@ -19,17 +19,21 @@ class HOTP(OTP):
19
19
  """
20
20
  return unicode(otp) == unicode(self.at(counter))
21
21
 
22
- def provisioning_uri(self, name, initial_count=0):
22
+ def provisioning_uri(self, name, initial_count=0, issuer_name=None):
23
23
  """
24
24
  Returns the provisioning URI for the OTP
25
25
  This can then be encoded in a QR Code and used
26
26
  to provision the Google Authenticator app
27
27
  @param [String] name of the account
28
28
  @param [Integer] initial_count starting counter value, defaults to 0
29
+ @param [String] the name of the OTP issuer; this will be the
30
+ organization title of the OTP entry in Authenticator
29
31
  @return [String] provisioning uri
30
32
  """
31
- return 'otpauth://hotp/%(name)s?secret=%(secret)s&counter=%(initial_count)s' % {
32
- 'name': urllib.quote(name, safe='@'),
33
- 'secret': self.secret,
34
- 'initial_count': initial_count,
35
- }
33
+ return utils.build_uri(
34
+ self.secret,
35
+ name,
36
+ initial_count=initial_count,
37
+ issuer_name=issuer_name,
38
+ )
39
+
@@ -1,7 +1,7 @@
1
1
  from pyotp.otp import OTP
2
+ from pyotp import utils
2
3
  import datetime
3
4
  import time
4
- import urllib
5
5
 
6
6
 
7
7
  class TOTP(OTP):
@@ -40,7 +40,7 @@ class TOTP(OTP):
40
40
 
41
41
  return unicode(otp) == unicode(self.at(for_time))
42
42
 
43
- def provisioning_uri(self, name):
43
+ def provisioning_uri(self, name, issuer_name=None):
44
44
  """
45
45
  Returns the provisioning URI for the OTP
46
46
  This can then be encoded in a QR Code and used
@@ -48,10 +48,7 @@ class TOTP(OTP):
48
48
  @param [String] name of the account
49
49
  @return [String] provisioning uri
50
50
  """
51
- return 'otpauth://totp/%(name)s?secret=%(secret)s' % {
52
- 'name': urllib.quote(name, safe='@'),
53
- 'secret': self.secret,
54
- }
51
+ return utils.build_uri(self.secret, name, issuer_name=issuer_name)
55
52
 
56
53
  def timecode(self, for_time):
57
54
  i = time.mktime(for_time.timetuple())
@@ -0,0 +1,46 @@
1
+ import urllib
2
+
3
+
4
+ def build_uri(secret, name, initial_count=None, issuer_name=None):
5
+ """
6
+ Returns the provisioning URI for the OTP; works for either TOTP or HOTP.
7
+
8
+ This can then be encoded in a QR Code and used to provision the Google
9
+ Authenticator app.
10
+
11
+ For module-internal use.
12
+
13
+ See also:
14
+ http://code.google.com/p/google-authenticator/wiki/KeyUriFormat
15
+
16
+ @param [String] the hotp/totp secret used to generate the URI
17
+ @param [String] name of the account
18
+ @param [Integer] initial_count starting counter value, defaults to None.
19
+ If none, the OTP type will be assumed as TOTP.
20
+ @param [String] the name of the OTP issuer; this will be the
21
+ organization title of the OTP entry in Authenticator
22
+ @return [String] provisioning uri
23
+ """
24
+ # initial_count may be 0 as a valid param
25
+ is_initial_count_present = (initial_count != None)
26
+
27
+ otp_type = 'hotp' if is_initial_count_present else 'totp'
28
+ base = 'otpauth://%s/' % otp_type
29
+
30
+ if issuer_name:
31
+ issuer_name = urllib.quote(issuer_name)
32
+ base += '%s:' % issuer_name
33
+
34
+ uri = '%(base)s%(name)s?secret=%(secret)s' % {
35
+ 'name': urllib.quote(name, safe='@'),
36
+ 'secret': secret,
37
+ 'base': base,
38
+ }
39
+
40
+ if is_initial_count_present:
41
+ uri += '&counter=%s' % initial_count
42
+
43
+ if issuer_name:
44
+ uri += '&issuer=%s' % issuer_name
45
+
46
+ return uri
@@ -32,7 +32,18 @@ class HOTPExampleValuesFromTheRFC(unittest.TestCase):
32
32
 
33
33
  def testProvisioningURI(self):
34
34
  hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr')
35
- self.assertEqual(hotp.provisioning_uri('mark@percival'), 'otpauth://hotp/mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0')
35
+
36
+ self.assertEqual(
37
+ hotp.provisioning_uri('mark@percival'),
38
+ 'otpauth://hotp/mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0')
39
+
40
+ self.assertEqual(
41
+ hotp.provisioning_uri('mark@percival', initial_count=12),
42
+ 'otpauth://hotp/mark@percival?secret=wrn3pqx5uqxqvnqr&counter=12')
43
+
44
+ self.assertEqual(
45
+ hotp.provisioning_uri('mark@percival', issuer_name='FooCorp!'),
46
+ 'otpauth://hotp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&counter=0&issuer=FooCorp%21')
36
47
 
37
48
 
38
49
  class TOTPExampleValuesFromTheRFC(unittest.TestCase):
@@ -57,7 +68,13 @@ class TOTPExampleValuesFromTheRFC(unittest.TestCase):
57
68
 
58
69
  def testProvisioningURI(self):
59
70
  totp = pyotp.TOTP('wrn3pqx5uqxqvnqr')
60
- self.assertEqual(totp.provisioning_uri('mark@percival'), 'otpauth://totp/mark@percival?secret=wrn3pqx5uqxqvnqr')
71
+ self.assertEqual(
72
+ totp.provisioning_uri('mark@percival'),
73
+ 'otpauth://totp/mark@percival?secret=wrn3pqx5uqxqvnqr')
74
+
75
+ self.assertEqual(
76
+ totp.provisioning_uri('mark@percival', issuer_name='FooCorp!'),
77
+ 'otpauth://totp/FooCorp%21:mark@percival?secret=wrn3pqx5uqxqvnqr&issuer=FooCorp%21')
61
78
 
62
79
 
63
80
  class Timecop(object):
File without changes
File without changes
File without changes