Skip to content

Commit 5384afc

Browse files
authored
Merge pull request #44 from jan-janssen/pyotp
Switch from otpauth to pyotp
2 parents 35bafd9 + 88ab87f commit 5384afc

10 files changed

+58
-107
lines changed

.binder/environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ channels:
33
dependencies:
44
- python
55
- qrcode
6-
- otpauth
6+
- pyotp
77
- pyzbar
88
- zbar
99
- pillow

.ci_support/environment.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ dependencies:
44
- python
55
- coverage
66
- qrcode=7.4.2
7-
- otpauth=2.0.0
7+
- pyotp=2.6.0
88
- pyzbar=0.1.9
99
- zbar=0.10
1010
- pillow=10.0.0

pyauthenticator/__init__.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
"""
22
Generate two factor authentication codes on the command line
33
"""
4-
from pyauthenticator.share import (
5-
load_config,
6-
generate_qrcode,
7-
get_two_factor_code as get_two_factor_code_internal,
8-
)
4+
from pyauthenticator.share import generate_qrcode
5+
from pyauthenticator.share import get_two_factor_code as get_two_factor_code_internal
6+
from pyauthenticator.share import load_config
97

108

119
def write_qrcode_to_file(service, file_name=None):

pyauthenticator/__main__.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
"""
44
import argparse
55
import sys
6+
67
from pyauthenticator.share import (
7-
list_services,
8-
load_config,
9-
generate_qrcode,
108
add_service,
9+
generate_qrcode,
1110
get_two_factor_code,
11+
list_services,
12+
load_config,
1213
)
1314

1415

pyauthenticator/share.py

+21-55
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
"""
22
Shared functionality to generate two factor authentication codes
33
"""
4-
import base64
54
import json
65
import os
7-
import otpauth
6+
from inspect import signature
7+
8+
import pyotp
9+
import qrcode
810
from PIL import Image
911
from pyzbar.pyzbar import decode
10-
import qrcode
11-
1212

1313
# default configuration file
1414
config_file = "~/.pyauthenticator"
@@ -75,28 +75,6 @@ def get_otpauth_dict(otpauth_str):
7575
}
7676

7777

78-
def add_padding(main_str, padding_str, padding_length, inverse_padding=False):
79-
"""
80-
Add padding to a string either in the beginning or at the end
81-
82-
Args:
83-
main_str (str): string to add padding to
84-
padding_str (str): padding character as string
85-
padding_length (int): the length of the final string should be a multiple of the padding length
86-
inverse_padding (bool): add padding in the beginning rather than the end
87-
88-
Returns:
89-
str: resulting string with padding
90-
"""
91-
missing_padding = len(main_str) % padding_length
92-
if missing_padding:
93-
if inverse_padding:
94-
main_str = padding_str * (padding_length - missing_padding) + main_str
95-
else:
96-
main_str += padding_str * (padding_length - missing_padding)
97-
return main_str
98-
99-
10078
def check_if_key_in_config(key, config_dict):
10179
"""
10280
Check if a given key is included in a dictionary, raise an ValueError if it is not.
@@ -122,37 +100,25 @@ def get_two_factor_code(key, config_dict):
122100
"""
123101
check_if_key_in_config(key=key, config_dict=config_dict)
124102
decode_dict_internal = get_otpauth_dict(otpauth_str=config_dict[key])
103+
funct_sig = signature(pyotp.TOTP)
104+
if "digits" in decode_dict_internal.keys():
105+
digits = int(decode_dict_internal["digits"])
106+
else:
107+
digits = funct_sig.parameters["digits"].default
125108
if "period" in decode_dict_internal.keys():
126-
totp = otpauth.TOTP(
127-
secret=base64.b32decode(
128-
add_padding(
129-
main_str=decode_dict_internal["secret"],
130-
padding_str="=",
131-
padding_length=8,
132-
inverse_padding=False,
133-
),
134-
True,
135-
),
136-
period=int(decode_dict_internal["period"]),
137-
)
109+
interval = int(decode_dict_internal["period"])
110+
else:
111+
interval = funct_sig.parameters["interval"].default
112+
if "issuer" in decode_dict_internal.keys():
113+
issuer = decode_dict_internal["issuer"]
138114
else:
139-
totp = otpauth.TOTP(
140-
secret=base64.b32decode(
141-
add_padding(
142-
main_str=decode_dict_internal["secret"],
143-
padding_str="=",
144-
padding_length=8,
145-
inverse_padding=False,
146-
),
147-
True,
148-
),
149-
)
150-
return add_padding(
151-
main_str=str(totp.string_code(totp.generate())),
152-
padding_str="0",
153-
padding_length=6,
154-
inverse_padding=True,
155-
)
115+
issuer = funct_sig.parameters["issuer"].default
116+
return pyotp.TOTP(
117+
s=decode_dict_internal["secret"],
118+
digits=digits,
119+
issuer=issuer,
120+
interval=interval,
121+
).now()
156122

157123

158124
def add_service(

setup.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
"""
22
Setuptools based setup module
33
"""
4-
from setuptools import setup, find_packages
5-
import versioneer
4+
from setuptools import find_packages, setup
65

6+
import versioneer
77

88
setup(
99
name='pyauthenticator',
@@ -17,7 +17,7 @@
1717
license='BSD',
1818
packages=find_packages(exclude=["*tests*"]),
1919
install_requires=[
20-
'otpauth==2.0.0',
20+
'pyotp==2.6.0',
2121
'qrcode==7.4.2',
2222
'pyzbar==0.1.9',
2323
'pillow==10.0.0',

tests/test_cmd.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import unittest
1+
import json
22
import os
33
import subprocess
4-
import json
4+
import unittest
55
from contextlib import redirect_stdout
66
from io import StringIO
7-
from pyauthenticator.share import expand_path, write_config, config_file
7+
88
from pyauthenticator.__main__ import command_line_parser
9+
from pyauthenticator.share import config_file, expand_path, write_config
910

1011

1112
class CmdSubprocessTest(unittest.TestCase):

tests/test_core.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
"""
22
Test for core functionality
33
"""
4-
import unittest
5-
from pyauthenticator.share import list_services, load_config, generate_qrcode, add_service, get_two_factor_code
64
import os
5+
import unittest
6+
7+
from pyauthenticator.share import (
8+
add_service,
9+
generate_qrcode,
10+
get_two_factor_code,
11+
list_services,
12+
load_config
13+
)
714

815

916
class TestCore(unittest.TestCase):

tests/test_share.py

+9-32
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
"""
22
Test for shared functionality
33
"""
4-
import unittest
54
import os
6-
from pyauthenticator.share import \
7-
expand_path, \
8-
load_config, \
9-
write_config, \
10-
get_otpauth_dict, \
11-
add_padding, \
12-
check_if_key_in_config
5+
import unittest
6+
7+
from pyauthenticator.share import (
8+
check_if_key_in_config,
9+
expand_path,
10+
get_otpauth_dict,
11+
load_config,
12+
write_config
13+
)
1314

1415

1516
class ShareTest(unittest.TestCase):
@@ -52,30 +53,6 @@ def test_get_otpauth_dict(self):
5253
}
5354
self.assertDictEqual(otpauth_dict, otp_test_dict)
5455

55-
def test_add_padding(self):
56-
main_str = "1234"
57-
padding_str = "0"
58-
str_pad_0 = add_padding(
59-
main_str=main_str,
60-
padding_str=padding_str,
61-
padding_length=5
62-
)
63-
str_pad_1 = add_padding(
64-
main_str=main_str,
65-
padding_str=padding_str,
66-
padding_length=6,
67-
inverse_padding=True
68-
)
69-
str_pad_2 = add_padding(
70-
main_str=main_str,
71-
padding_str=padding_str,
72-
padding_length=7,
73-
inverse_padding=False
74-
)
75-
self.assertEqual("12340", str_pad_0)
76-
self.assertEqual("001234", str_pad_1)
77-
self.assertEqual("1234000", str_pad_2)
78-
7956
def test_check_if_key_in_config(self):
8057
test_dict = {"key": "value"}
8158
check_if_key_in_config(

tests/test_user_interface.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import unittest
21
import os
2+
import unittest
3+
34
from pyauthenticator import get_two_factor_code, write_qrcode_to_file
4-
from pyauthenticator.share import expand_path, write_config, config_file
5+
from pyauthenticator.share import config_file, expand_path, write_config
56

67

78
class TestUserInterface(unittest.TestCase):

0 commit comments

Comments
 (0)