pyauthenticator 0.2.1__tar.gz → 0.3.0__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.
- pyauthenticator-0.3.0/.gitignore +13 -0
- {pyauthenticator-0.2.1 → pyauthenticator-0.3.0}/PKG-INFO +5 -5
- {pyauthenticator-0.2.1 → pyauthenticator-0.3.0}/README.md +4 -4
- {pyauthenticator-0.2.1 → pyauthenticator-0.3.0}/pyproject.toml +10 -6
- pyauthenticator-0.3.0/src/pyauthenticator/__init__.py +19 -0
- pyauthenticator-0.3.0/src/pyauthenticator/__main__.py +9 -0
- pyauthenticator-0.2.1/pyauthenticator/__main__.py → pyauthenticator-0.3.0/src/pyauthenticator/_cmd.py +33 -24
- pyauthenticator-0.3.0/src/pyauthenticator/_config.py +42 -0
- pyauthenticator-0.3.0/src/pyauthenticator/_core.py +64 -0
- pyauthenticator-0.3.0/src/pyauthenticator/_user.py +65 -0
- {pyauthenticator-0.2.1 → pyauthenticator-0.3.0/src}/pyauthenticator/_version.py +16 -3
- pyauthenticator-0.3.0/src/pyauthenticator/api.py +70 -0
- pyauthenticator-0.2.1/pyauthenticator/__init__.py +0 -37
- pyauthenticator-0.2.1/pyauthenticator/share.py +0 -167
- {pyauthenticator-0.2.1 → pyauthenticator-0.3.0}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyauthenticator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Similar to the Google authenticator just written in python.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jan-janssen/pyauthenticator
|
|
6
6
|
Project-URL: Documentation, https://github.com/jan-janssen/pyauthenticator/blob/master/README.md
|
|
@@ -32,7 +32,7 @@ Requires-Dist: qrcode>=8.2
|
|
|
32
32
|
Description-Content-Type: text/markdown
|
|
33
33
|
|
|
34
34
|
# pyauthenticator
|
|
35
|
-
[](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml)
|
|
36
36
|
[](https://coveralls.io/github/jan-janssen/pyauthenticator?branch=main)
|
|
37
37
|
[](https://github.com/psf/black)
|
|
38
38
|
|
|
@@ -107,7 +107,7 @@ For any support requests feel free to open an [issue on Github](https://github.c
|
|
|
107
107
|
# For Developers
|
|
108
108
|
## Python Interface
|
|
109
109
|
The same functionality which is available on the command line is also available via the python interface:
|
|
110
|
-
```
|
|
110
|
+
```python
|
|
111
111
|
from pyauthenticator import get_two_factor_code
|
|
112
112
|
get_two_factor_code(service)
|
|
113
113
|
```
|
|
@@ -117,10 +117,10 @@ factor authentication.
|
|
|
117
117
|
## Configuration
|
|
118
118
|
The configuration is stored in `~/.pyauthenticator` it is written in the JSON format. For a given service like `github`
|
|
119
119
|
the config file contains:
|
|
120
|
-
```
|
|
120
|
+
```JSON
|
|
121
121
|
{"google": "otpauth://totp/Google:<username>?secret=<secret>&issuer=Google"}
|
|
122
122
|
```
|
|
123
123
|
With the Google username `<username>` and the corresponding secret `<secret>` being contained in the QR code.
|
|
124
124
|
|
|
125
125
|
## License
|
|
126
|
-
The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
|
|
126
|
+
The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# pyauthenticator
|
|
2
|
-
[](https://github.com/jan-janssen/pyauthenticator/actions/workflows/pipeline.yml)
|
|
3
3
|
[](https://coveralls.io/github/jan-janssen/pyauthenticator?branch=main)
|
|
4
4
|
[](https://github.com/psf/black)
|
|
5
5
|
|
|
@@ -74,7 +74,7 @@ For any support requests feel free to open an [issue on Github](https://github.c
|
|
|
74
74
|
# For Developers
|
|
75
75
|
## Python Interface
|
|
76
76
|
The same functionality which is available on the command line is also available via the python interface:
|
|
77
|
-
```
|
|
77
|
+
```python
|
|
78
78
|
from pyauthenticator import get_two_factor_code
|
|
79
79
|
get_two_factor_code(service)
|
|
80
80
|
```
|
|
@@ -84,10 +84,10 @@ factor authentication.
|
|
|
84
84
|
## Configuration
|
|
85
85
|
The configuration is stored in `~/.pyauthenticator` it is written in the JSON format. For a given service like `github`
|
|
86
86
|
the config file contains:
|
|
87
|
-
```
|
|
87
|
+
```JSON
|
|
88
88
|
{"google": "otpauth://totp/Google:<username>?secret=<secret>&issuer=Google"}
|
|
89
89
|
```
|
|
90
90
|
With the Google username `<username>` and the corresponding secret `<secret>` being contained in the QR code.
|
|
91
91
|
|
|
92
92
|
## License
|
|
93
|
-
The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
|
|
93
|
+
The `pyauthenticator` package is licensed under the [BSD-3-Clause license](https://github.com/jan-janssen/pyauthenticator/blob/main/LICENSE).
|
|
@@ -50,26 +50,30 @@ Source = "https://github.com/jan-janssen/pyauthenticator"
|
|
|
50
50
|
Tracker = "https://github.com/jan-janssen/pyauthenticator/issues"
|
|
51
51
|
|
|
52
52
|
[project.scripts]
|
|
53
|
-
pyauthenticator = "pyauthenticator.
|
|
53
|
+
pyauthenticator = "pyauthenticator._cmd:command_line_parser"
|
|
54
54
|
|
|
55
55
|
[tool.hatch.build]
|
|
56
56
|
include = [
|
|
57
|
-
"pyauthenticator"
|
|
57
|
+
"src/pyauthenticator"
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
[tool.hatch.build.hooks.vcs]
|
|
61
|
-
version-file = "pyauthenticator/_version.py"
|
|
61
|
+
version-file = "src/pyauthenticator/_version.py"
|
|
62
62
|
|
|
63
63
|
[tool.hatch.build.targets.sdist]
|
|
64
64
|
include = [
|
|
65
|
-
"pyauthenticator"
|
|
65
|
+
"src/pyauthenticator"
|
|
66
66
|
]
|
|
67
67
|
|
|
68
68
|
[tool.hatch.build.targets.wheel]
|
|
69
69
|
packages = [
|
|
70
|
-
"pyauthenticator"
|
|
70
|
+
"src/pyauthenticator"
|
|
71
71
|
]
|
|
72
72
|
|
|
73
73
|
[tool.hatch.version]
|
|
74
74
|
source = "vcs"
|
|
75
|
-
path = "pyauthenticator/_version.py"
|
|
75
|
+
path = "src/pyauthenticator/_version.py"
|
|
76
|
+
|
|
77
|
+
[tool.coverage.run]
|
|
78
|
+
source = ["pyauthenticator"]
|
|
79
|
+
command_line = "-m unittest discover tests"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generate two factor authentication codes on the command line
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from . import _version
|
|
6
|
+
from pyauthenticator._user import (
|
|
7
|
+
write_qrcode_to_file,
|
|
8
|
+
get_two_factor_code,
|
|
9
|
+
add_two_factor_provider,
|
|
10
|
+
list_two_factor_providers,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"write_qrcode_to_file",
|
|
15
|
+
"get_two_factor_code",
|
|
16
|
+
"add_two_factor_provider",
|
|
17
|
+
"list_two_factor_providers",
|
|
18
|
+
]
|
|
19
|
+
__version__: str = _version.__version__
|
|
@@ -4,17 +4,18 @@ Generate two factor authentication codes on the command line
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import sys
|
|
7
|
+
from typing import Optional, Sequence
|
|
7
8
|
|
|
8
|
-
from pyauthenticator.
|
|
9
|
+
from pyauthenticator._config import load_config
|
|
10
|
+
from pyauthenticator.api import (
|
|
9
11
|
add_service,
|
|
10
12
|
generate_qrcode,
|
|
11
|
-
|
|
13
|
+
get_totp_for_key_in_dict,
|
|
12
14
|
list_services,
|
|
13
|
-
load_config,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def command_line_parser(cmd_args=None):
|
|
18
|
+
def command_line_parser(cmd_args: Optional[Sequence[str]] = None) -> None:
|
|
18
19
|
"""
|
|
19
20
|
Main function primarly used for the command line interface
|
|
20
21
|
"""
|
|
@@ -46,8 +47,14 @@ def command_line_parser(cmd_args=None):
|
|
|
46
47
|
)
|
|
47
48
|
args = parser.parse_args(args=cmd_args)
|
|
48
49
|
if args.qrcode:
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
try:
|
|
51
|
+
generate_qrcode(key=args.service, config_dict=config_dict)
|
|
52
|
+
except KeyError:
|
|
53
|
+
_print_service_does_not_exists(
|
|
54
|
+
config_dict=config_dict, service=args.service
|
|
55
|
+
)
|
|
56
|
+
else:
|
|
57
|
+
print("The qrcode file <" + args.service + ".png> was generated.")
|
|
51
58
|
elif args.add:
|
|
52
59
|
add_service(
|
|
53
60
|
key=args.service, qrcode_png_file_name=args.add, config_dict=config_dict
|
|
@@ -61,23 +68,25 @@ def command_line_parser(cmd_args=None):
|
|
|
61
68
|
)
|
|
62
69
|
else:
|
|
63
70
|
try:
|
|
64
|
-
print(
|
|
65
|
-
except
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
+ args.service
|
|
70
|
-
+ '" does not exist.\n\nThe config file ~/.pyauthenticator contains the following services:'
|
|
71
|
-
)
|
|
72
|
-
for service in list_services(config_dict=config_dict):
|
|
73
|
-
print(" * " + service)
|
|
74
|
-
print("\nChoose one of these or add a new service using:")
|
|
75
|
-
else:
|
|
76
|
-
print(
|
|
77
|
-
"The config file ~/.pyauthenticator does not contain any services. To add a new service use:"
|
|
78
|
-
)
|
|
79
|
-
print(" pyauthenticator --add <qr-code.png> <servicename>\n")
|
|
71
|
+
print(get_totp_for_key_in_dict(key=args.service, config_dict=config_dict))
|
|
72
|
+
except KeyError:
|
|
73
|
+
_print_service_does_not_exists(
|
|
74
|
+
config_dict=config_dict, service=args.service
|
|
75
|
+
)
|
|
80
76
|
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
def _print_service_does_not_exists(config_dict: dict, service: str) -> None:
|
|
79
|
+
if len(config_dict) > 0:
|
|
80
|
+
print(
|
|
81
|
+
'The service "'
|
|
82
|
+
+ service
|
|
83
|
+
+ '" does not exist.\n\nThe config file ~/.pyauthenticator contains the following services:'
|
|
84
|
+
)
|
|
85
|
+
for service_in_config in list_services(config_dict=config_dict):
|
|
86
|
+
print(" * " + service_in_config)
|
|
87
|
+
print("\nChoose one of these or add a new service using:")
|
|
88
|
+
else:
|
|
89
|
+
print(
|
|
90
|
+
"The config file ~/.pyauthenticator does not contain any services. To add a new service use:"
|
|
91
|
+
)
|
|
92
|
+
print(" pyauthenticator --add <qr-code.png> <servicename>\n")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration file handling for pyauthenticator
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from os.path import exists, abspath, expanduser
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
# default configuration file
|
|
10
|
+
default_config_file: str = "~/.pyauthenticator"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_config(config_file_to_load: str = default_config_file) -> Dict[str, Any]:
|
|
14
|
+
"""
|
|
15
|
+
Load configuration file
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
config_file_to_load (str): path to config file
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
dict: Dictionary with service names as keys and the otpauth url as values
|
|
22
|
+
"""
|
|
23
|
+
abs_config_path = abspath(expanduser(config_file_to_load))
|
|
24
|
+
if exists(abs_config_path):
|
|
25
|
+
with open(abs_config_path, "r") as f:
|
|
26
|
+
return json.load(f)
|
|
27
|
+
else:
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def write_config(
|
|
32
|
+
config_dict: Dict[str, Any], config_file_to_write: str = default_config_file
|
|
33
|
+
) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Write configuration file
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
config_dict (dict): configuration dictionary
|
|
39
|
+
config_file_to_write (str): path to config file
|
|
40
|
+
"""
|
|
41
|
+
with open(abspath(expanduser(config_file_to_write)), "w") as f:
|
|
42
|
+
json.dump(config_dict, f)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core functionality to generate two factor authentication codes
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import pyotp
|
|
8
|
+
import qrcode
|
|
9
|
+
from PIL import Image
|
|
10
|
+
from pyzbar.pyzbar import decode
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def decode_qrcode(qrcode_png_file_name: str) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Decode qrcode from png file
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
qrcode_png_file_name (str): path to the png file which contains the qr code
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
str: decoded otpauth string
|
|
22
|
+
"""
|
|
23
|
+
return decode(Image.open(qrcode_png_file_name))[0].data.decode("utf-8")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def encode_qrcode(otpauth_str: str, file_name: str) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Encode otpauth string into qrcode image saved as png file
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
otpauth_str (str): otpauth string
|
|
32
|
+
file_name (str): path to the png file which will contain the qr code
|
|
33
|
+
"""
|
|
34
|
+
qrcode.make(otpauth_str).save(file_name, "PNG")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_totp(otpauth_str: str) -> str:
|
|
38
|
+
"""
|
|
39
|
+
Get TOTP code for a specific service based on its otpauth dictionary
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
otpauth_str (str): otpauth string for the service
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: TOTP code for the service
|
|
46
|
+
"""
|
|
47
|
+
otpauth_dict = {
|
|
48
|
+
kv[0]: kv[1]
|
|
49
|
+
for kv in [
|
|
50
|
+
otpvar.split("=") for otpvar in otpauth_str.replace("?", "&").split("&")[1:]
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
kwargs: dict[str, Any] = {}
|
|
55
|
+
if "digits" in otpauth_dict.keys():
|
|
56
|
+
kwargs["digits"] = int(otpauth_dict["digits"])
|
|
57
|
+
if "period" in otpauth_dict.keys():
|
|
58
|
+
kwargs["interval"] = int(otpauth_dict["period"])
|
|
59
|
+
if "issuer" in otpauth_dict.keys():
|
|
60
|
+
kwargs["issuer"] = otpauth_dict["issuer"]
|
|
61
|
+
return pyotp.TOTP(
|
|
62
|
+
**kwargs,
|
|
63
|
+
s=otpauth_dict["secret"],
|
|
64
|
+
).now()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generate two factor authentication codes on the command line
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from pyauthenticator._config import default_config_file, load_config
|
|
7
|
+
from pyauthenticator.api import (
|
|
8
|
+
add_service,
|
|
9
|
+
generate_qrcode,
|
|
10
|
+
get_totp_for_key_in_dict,
|
|
11
|
+
list_services,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def write_qrcode_to_file(service: str, file_name: Optional[str] = None) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Write qrcode to file to scan it with a mobile application
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
service (str): lower case name of the service
|
|
21
|
+
file_name (str/ None): default file name <service.png>
|
|
22
|
+
"""
|
|
23
|
+
generate_qrcode(key=service, config_dict=load_config(), file_name=file_name)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_two_factor_code(service: str) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Generate two factor authentication code
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
service (str): lower case name of the service
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: two factor authentication code
|
|
35
|
+
"""
|
|
36
|
+
return get_totp_for_key_in_dict(
|
|
37
|
+
key=service,
|
|
38
|
+
config_dict=load_config(),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_two_factor_provider(service: str, qrcode_png_file_name: str) -> None:
|
|
43
|
+
"""
|
|
44
|
+
Add new two factor authentication provider to configuration file
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
service (str): lower case name of the service
|
|
48
|
+
qrcode_png_file_name (str): path to the png file which contains the qr code
|
|
49
|
+
"""
|
|
50
|
+
add_service(
|
|
51
|
+
key=service,
|
|
52
|
+
qrcode_png_file_name=qrcode_png_file_name,
|
|
53
|
+
config_dict=load_config(),
|
|
54
|
+
config_file_to_write=default_config_file,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def list_two_factor_providers() -> list[str]:
|
|
59
|
+
"""
|
|
60
|
+
List all two factor authentication providers in the configuration file
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
list[str]: list of two factor authentication providers
|
|
64
|
+
"""
|
|
65
|
+
return list_services(config_dict=load_config())
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# file generated by setuptools-scm
|
|
2
2
|
# don't change, don't track in version control
|
|
3
3
|
|
|
4
|
-
__all__ = [
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
5
12
|
|
|
6
13
|
TYPE_CHECKING = False
|
|
7
14
|
if TYPE_CHECKING:
|
|
@@ -9,13 +16,19 @@ if TYPE_CHECKING:
|
|
|
9
16
|
from typing import Union
|
|
10
17
|
|
|
11
18
|
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
12
20
|
else:
|
|
13
21
|
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
14
23
|
|
|
15
24
|
version: str
|
|
16
25
|
__version__: str
|
|
17
26
|
__version_tuple__: VERSION_TUPLE
|
|
18
27
|
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
19
30
|
|
|
20
|
-
__version__ = version = '0.
|
|
21
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.3.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared functionality to generate two factor authentication codes
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from pyauthenticator._core import decode_qrcode, encode_qrcode, get_totp
|
|
8
|
+
from pyauthenticator._config import default_config_file, write_config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_totp_for_key_in_dict(key: str, config_dict: Dict[str, Any]) -> str:
|
|
12
|
+
"""
|
|
13
|
+
Generate the two factor authentication code
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
key (str): lower case name of the service
|
|
17
|
+
config_dict (dict): configuration dictionary
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
str: two factor authentication code as string
|
|
21
|
+
"""
|
|
22
|
+
return get_totp(otpauth_str=config_dict[key])
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def add_service(
|
|
26
|
+
key: str,
|
|
27
|
+
qrcode_png_file_name: str,
|
|
28
|
+
config_dict: Dict[str, Any],
|
|
29
|
+
config_file_to_write: str = default_config_file,
|
|
30
|
+
) -> None:
|
|
31
|
+
"""
|
|
32
|
+
Add new service to configuration file
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
key (str): lower case name of the service
|
|
36
|
+
qrcode_png_file_name (str): path to the png file which contains the qr code
|
|
37
|
+
config_dict (dict): configuration dictionary
|
|
38
|
+
config_file_to_write (str): path to config file
|
|
39
|
+
"""
|
|
40
|
+
config_dict[key] = decode_qrcode(qrcode_png_file_name=qrcode_png_file_name)
|
|
41
|
+
write_config(config_dict=config_dict, config_file_to_write=config_file_to_write)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def generate_qrcode(
|
|
45
|
+
key: str, config_dict: Dict[str, Any], file_name: Optional[str] = None
|
|
46
|
+
) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Write qrcode to file to scan it with a mobile application
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
key (str): lower case name of the service
|
|
52
|
+
config_dict (dict): configuration dictionary
|
|
53
|
+
file_name (str/ None): default file name <service.png>
|
|
54
|
+
"""
|
|
55
|
+
if file_name is None:
|
|
56
|
+
file_name = key + ".png"
|
|
57
|
+
encode_qrcode(otpauth_str=config_dict[key], file_name=file_name)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def list_services(config_dict: Dict[str, Any]) -> List[str]:
|
|
61
|
+
"""
|
|
62
|
+
List available services
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
config_dict (dict): configuration dictionary
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
list: list of available services
|
|
69
|
+
"""
|
|
70
|
+
return list(config_dict.keys())
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Generate two factor authentication codes on the command line
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from . import _version
|
|
6
|
-
from pyauthenticator.share import generate_qrcode
|
|
7
|
-
from pyauthenticator.share import get_two_factor_code as get_two_factor_code_internal
|
|
8
|
-
from pyauthenticator.share import load_config
|
|
9
|
-
|
|
10
|
-
__version__ = _version.__version__
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def write_qrcode_to_file(service, file_name=None):
|
|
14
|
-
"""
|
|
15
|
-
Write qrcode to file to scan it with a mobile application
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
service (str): lower case name of the service
|
|
19
|
-
file_name (str/ None): default file name <service.png>
|
|
20
|
-
"""
|
|
21
|
-
generate_qrcode(key=service, config_dict=load_config(), file_name=file_name)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def get_two_factor_code(service):
|
|
25
|
-
"""
|
|
26
|
-
Generate two factor authentication code
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
service (str): lower case name of the service
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
str: two factor authentication code
|
|
33
|
-
"""
|
|
34
|
-
return get_two_factor_code_internal(
|
|
35
|
-
key=service,
|
|
36
|
-
config_dict=load_config(),
|
|
37
|
-
)
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Shared functionality to generate two factor authentication codes
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import os
|
|
7
|
-
from inspect import signature
|
|
8
|
-
|
|
9
|
-
import pyotp
|
|
10
|
-
import qrcode
|
|
11
|
-
from PIL import Image
|
|
12
|
-
from pyzbar.pyzbar import decode
|
|
13
|
-
|
|
14
|
-
# default configuration file
|
|
15
|
-
config_file = "~/.pyauthenticator"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def expand_path(path):
|
|
19
|
-
"""
|
|
20
|
-
Expand path by expanding the user variable and converting the path to an absolute path
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
path (str): path before expansion
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
str: expanded path
|
|
27
|
-
"""
|
|
28
|
-
return os.path.abspath(os.path.expanduser(path))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def load_config(config_file_to_load=config_file):
|
|
32
|
-
"""
|
|
33
|
-
Load configuration file
|
|
34
|
-
|
|
35
|
-
Args:
|
|
36
|
-
config_file_to_load (str): path to config file
|
|
37
|
-
|
|
38
|
-
Returns:
|
|
39
|
-
dict: Dictionary with service names as keys and the otpauth url as values
|
|
40
|
-
"""
|
|
41
|
-
abs_config_path = expand_path(path=config_file_to_load)
|
|
42
|
-
if os.path.exists(abs_config_path):
|
|
43
|
-
with open(abs_config_path, "r") as f:
|
|
44
|
-
return json.load(f)
|
|
45
|
-
else:
|
|
46
|
-
return {}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def write_config(config_dict, config_file_to_write=config_file):
|
|
50
|
-
"""
|
|
51
|
-
Write configuration file
|
|
52
|
-
|
|
53
|
-
Args:
|
|
54
|
-
config_dict (dict): configuration dictionary
|
|
55
|
-
config_file_to_write (str): path to config file
|
|
56
|
-
"""
|
|
57
|
-
with open(expand_path(path=config_file_to_write), "w") as f:
|
|
58
|
-
json.dump(config_dict, f)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def get_otpauth_dict(otpauth_str):
|
|
62
|
-
"""
|
|
63
|
-
Parse otpauth url
|
|
64
|
-
|
|
65
|
-
Args:
|
|
66
|
-
otpauth_str (str): otpauth url as string
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
dict: Dictionary with the parameters of the otpauth url as key-value pairs
|
|
70
|
-
"""
|
|
71
|
-
return {
|
|
72
|
-
kv[0]: kv[1]
|
|
73
|
-
for kv in [
|
|
74
|
-
otpvar.split("=") for otpvar in otpauth_str.replace("?", "&").split("&")[1:]
|
|
75
|
-
]
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def check_if_key_in_config(key, config_dict):
|
|
80
|
-
"""
|
|
81
|
-
Check if a given key is included in a dictionary, raise an ValueError if it is not.
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
key (str): key as string
|
|
85
|
-
config_dict (dict): configuration dictionary
|
|
86
|
-
"""
|
|
87
|
-
if key not in config_dict.keys():
|
|
88
|
-
raise ValueError()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def get_two_factor_code(key, config_dict):
|
|
92
|
-
"""
|
|
93
|
-
Generate the two factor authentication code
|
|
94
|
-
|
|
95
|
-
Args:
|
|
96
|
-
key (str): lower case name of the service
|
|
97
|
-
config_dict (dict): configuration dictionary
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
str: two factor authentication code as string
|
|
101
|
-
"""
|
|
102
|
-
check_if_key_in_config(key=key, config_dict=config_dict)
|
|
103
|
-
decode_dict_internal = get_otpauth_dict(otpauth_str=config_dict[key])
|
|
104
|
-
funct_sig = signature(pyotp.TOTP)
|
|
105
|
-
if "digits" in decode_dict_internal.keys():
|
|
106
|
-
digits = int(decode_dict_internal["digits"])
|
|
107
|
-
else:
|
|
108
|
-
digits = funct_sig.parameters["digits"].default
|
|
109
|
-
if "period" in decode_dict_internal.keys():
|
|
110
|
-
interval = int(decode_dict_internal["period"])
|
|
111
|
-
else:
|
|
112
|
-
interval = funct_sig.parameters["interval"].default
|
|
113
|
-
if "issuer" in decode_dict_internal.keys():
|
|
114
|
-
issuer = decode_dict_internal["issuer"]
|
|
115
|
-
else:
|
|
116
|
-
issuer = funct_sig.parameters["issuer"].default
|
|
117
|
-
return pyotp.TOTP(
|
|
118
|
-
s=decode_dict_internal["secret"],
|
|
119
|
-
digits=digits,
|
|
120
|
-
issuer=issuer,
|
|
121
|
-
interval=interval,
|
|
122
|
-
).now()
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def add_service(
|
|
126
|
-
key, qrcode_png_file_name, config_dict, config_file_to_write=config_file
|
|
127
|
-
):
|
|
128
|
-
"""
|
|
129
|
-
Add new service to configuration file
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
key (str): lower case name of the service
|
|
133
|
-
qrcode_png_file_name (str): path to the png file which contains the qr code
|
|
134
|
-
config_dict (dict): configuration dictionary
|
|
135
|
-
config_file_to_write (str): path to config file
|
|
136
|
-
"""
|
|
137
|
-
otpauth_str = decode(Image.open(qrcode_png_file_name))[0].data.decode("utf-8")
|
|
138
|
-
config_dict[key] = otpauth_str
|
|
139
|
-
write_config(config_dict=config_dict, config_file_to_write=config_file_to_write)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def generate_qrcode(key, config_dict, file_name=None):
|
|
143
|
-
"""
|
|
144
|
-
Write qrcode to file to scan it with a mobile application
|
|
145
|
-
|
|
146
|
-
Args:
|
|
147
|
-
key (str): lower case name of the service
|
|
148
|
-
config_dict (dict): configuration dictionary
|
|
149
|
-
file_name (str/ None): default file name <service.png>
|
|
150
|
-
"""
|
|
151
|
-
if file_name is None:
|
|
152
|
-
file_name = key + ".png"
|
|
153
|
-
check_if_key_in_config(key=key, config_dict=config_dict)
|
|
154
|
-
qrcode.make(config_dict[key]).save(file_name, "PNG")
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def list_services(config_dict):
|
|
158
|
-
"""
|
|
159
|
-
List available services
|
|
160
|
-
|
|
161
|
-
Args:
|
|
162
|
-
config_dict (dict): configuration dictionary
|
|
163
|
-
|
|
164
|
-
Returns:
|
|
165
|
-
list: list of available services
|
|
166
|
-
"""
|
|
167
|
-
return list(config_dict.keys())
|
|
File without changes
|