commit
7f899a57a8
23 changed files with 1628 additions and 0 deletions
@ -0,0 +1,209 @@ |
|||||
|
|
||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python,macos,linux,vim |
||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python,macos,linux,vim |
||||
|
|
||||
|
### Linux ### |
||||
|
*~ |
||||
|
|
||||
|
# temporary files which can be created if a process still has a handle open of a deleted file |
||||
|
.fuse_hidden* |
||||
|
|
||||
|
# KDE directory preferences |
||||
|
.directory |
||||
|
|
||||
|
# Linux trash folder which might appear on any partition or disk |
||||
|
.Trash-* |
||||
|
|
||||
|
# .nfs files are created when an open file is removed but is still being accessed |
||||
|
.nfs* |
||||
|
|
||||
|
### macOS ### |
||||
|
# General |
||||
|
.DS_Store |
||||
|
.AppleDouble |
||||
|
.LSOverride |
||||
|
|
||||
|
# Icon must end with two \r |
||||
|
Icon |
||||
|
|
||||
|
|
||||
|
# Thumbnails |
||||
|
._* |
||||
|
|
||||
|
# Files that might appear in the root of a volume |
||||
|
.DocumentRevisions-V100 |
||||
|
.fseventsd |
||||
|
.Spotlight-V100 |
||||
|
.TemporaryItems |
||||
|
.Trashes |
||||
|
.VolumeIcon.icns |
||||
|
.com.apple.timemachine.donotpresent |
||||
|
|
||||
|
# Directories potentially created on remote AFP share |
||||
|
.AppleDB |
||||
|
.AppleDesktop |
||||
|
Network Trash Folder |
||||
|
Temporary Items |
||||
|
.apdisk |
||||
|
|
||||
|
### Python ### |
||||
|
# Byte-compiled / optimized / DLL files |
||||
|
__pycache__/ |
||||
|
*.py[cod] |
||||
|
*$py.class |
||||
|
|
||||
|
# C extensions |
||||
|
*.so |
||||
|
|
||||
|
# Distribution / packaging |
||||
|
.Python |
||||
|
build/ |
||||
|
develop-eggs/ |
||||
|
dist/ |
||||
|
downloads/ |
||||
|
eggs/ |
||||
|
.eggs/ |
||||
|
lib/ |
||||
|
lib64/ |
||||
|
parts/ |
||||
|
sdist/ |
||||
|
var/ |
||||
|
wheels/ |
||||
|
share/python-wheels/ |
||||
|
*.egg-info/ |
||||
|
.installed.cfg |
||||
|
*.egg |
||||
|
MANIFEST |
||||
|
|
||||
|
# PyInstaller |
||||
|
# Usually these files are written by a python script from a template |
||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it. |
||||
|
*.manifest |
||||
|
*.spec |
||||
|
|
||||
|
# Installer logs |
||||
|
pip-log.txt |
||||
|
pip-delete-this-directory.txt |
||||
|
|
||||
|
# Unit test / coverage reports |
||||
|
htmlcov/ |
||||
|
.tox/ |
||||
|
.nox/ |
||||
|
.coverage |
||||
|
.coverage.* |
||||
|
.cache |
||||
|
nosetests.xml |
||||
|
coverage.xml |
||||
|
*.cover |
||||
|
*.py,cover |
||||
|
.hypothesis/ |
||||
|
.pytest_cache/ |
||||
|
cover/ |
||||
|
|
||||
|
# Translations |
||||
|
*.mo |
||||
|
*.pot |
||||
|
|
||||
|
# Django stuff: |
||||
|
*.log |
||||
|
local_settings.py |
||||
|
db.sqlite3 |
||||
|
db.sqlite3-journal |
||||
|
|
||||
|
# Flask stuff: |
||||
|
instance/ |
||||
|
.webassets-cache |
||||
|
|
||||
|
# Scrapy stuff: |
||||
|
.scrapy |
||||
|
|
||||
|
# Sphinx documentation |
||||
|
docs/_build/ |
||||
|
|
||||
|
# PyBuilder |
||||
|
.pybuilder/ |
||||
|
target/ |
||||
|
|
||||
|
# Jupyter Notebook |
||||
|
.ipynb_checkpoints |
||||
|
|
||||
|
# IPython |
||||
|
profile_default/ |
||||
|
ipython_config.py |
||||
|
|
||||
|
# pyenv |
||||
|
# For a library or package, you might want to ignore these files since the code is |
||||
|
# intended to run in multiple environments; otherwise, check them in: |
||||
|
# .python-version |
||||
|
|
||||
|
# pipenv |
||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. |
||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies |
||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not |
||||
|
# install all needed dependencies. |
||||
|
#Pipfile.lock |
||||
|
|
||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow |
||||
|
__pypackages__/ |
||||
|
|
||||
|
# Celery stuff |
||||
|
celerybeat-schedule |
||||
|
celerybeat.pid |
||||
|
|
||||
|
# SageMath parsed files |
||||
|
*.sage.py |
||||
|
|
||||
|
# Environments |
||||
|
.env |
||||
|
.venv |
||||
|
env/ |
||||
|
venv/ |
||||
|
ENV/ |
||||
|
env.bak/ |
||||
|
venv.bak/ |
||||
|
|
||||
|
# Spyder project settings |
||||
|
.spyderproject |
||||
|
.spyproject |
||||
|
|
||||
|
# Rope project settings |
||||
|
.ropeproject |
||||
|
|
||||
|
# mkdocs documentation |
||||
|
/site |
||||
|
|
||||
|
# mypy |
||||
|
.mypy_cache/ |
||||
|
.dmypy.json |
||||
|
dmypy.json |
||||
|
|
||||
|
# Pyre type checker |
||||
|
.pyre/ |
||||
|
|
||||
|
# pytype static type analyzer |
||||
|
.pytype/ |
||||
|
|
||||
|
# Cython debug symbols |
||||
|
cython_debug/ |
||||
|
|
||||
|
### Vim ### |
||||
|
# Swap |
||||
|
[._]*.s[a-v][a-z] |
||||
|
!*.svg # comment out if you don't need vector files |
||||
|
[._]*.sw[a-p] |
||||
|
[._]s[a-rt-v][a-z] |
||||
|
[._]ss[a-gi-z] |
||||
|
[._]sw[a-p] |
||||
|
|
||||
|
# Session |
||||
|
Session.vim |
||||
|
Sessionx.vim |
||||
|
|
||||
|
# Temporary |
||||
|
.netrwhist |
||||
|
# Auto-generated tag files |
||||
|
tags |
||||
|
# Persistent undo |
||||
|
[._]*.un~ |
||||
|
|
||||
|
# End of https://www.toptal.com/developers/gitignore/api/python,macos,linux,vim |
@ -0,0 +1,16 @@ |
|||||
|
# GAuth QR Reencoder |
||||
|
|
||||
|
This tool is used to conver image files from Google Authenticator's QR code from both migration (export) and new TOTP. |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
The script would take a screenshot of the screen in 2 seconds, then parse the first eligible QR code shown on the screen. |
||||
|
|
||||
|
Execute the following to start the Python script: |
||||
|
``` |
||||
|
python3 gauth_reencoder.py |
||||
|
``` |
||||
|
|
||||
|
## References |
||||
|
|
||||
|
This project utilises code from digitalduke (https://github.com/digitalduke/otpauth-migration-decoder) |
@ -0,0 +1,98 @@ |
|||||
|
from PIL import Image |
||||
|
import PIL |
||||
|
import pyzbar.pyzbar as pyzbar |
||||
|
import otpauth_migration_decoder.src.decoder |
||||
|
import urllib.parse |
||||
|
import time |
||||
|
import qrcode |
||||
|
|
||||
|
def decode_qr_file(img_file: str) -> str: |
||||
|
img = Image.open(img_file) |
||||
|
decoded = pyzbar.decode(img) |
||||
|
return decoded[0].data.decode("utf8") |
||||
|
|
||||
|
def decode_screenshot_img() -> str: |
||||
|
from PIL import ImageGrab |
||||
|
img = ImageGrab.grab() |
||||
|
decoded = pyzbar.decode(img) |
||||
|
return decoded[0].data.decode("utf8") |
||||
|
|
||||
|
def decode_totp(totp_uri: str) -> list: |
||||
|
issuer_start = totp_uri.split("&issuer=")[1] |
||||
|
issuer = issuer_start.split("&")[0] |
||||
|
|
||||
|
secret_start = totp_uri.split("&secret=")[1] |
||||
|
secret = secret_start.split("&")[0] |
||||
|
|
||||
|
algorithm_start = totp_uri.split("?algorithm=")[1] |
||||
|
algorithm = algorithm_start.split("&")[0] |
||||
|
|
||||
|
account_start = totp_uri.split("totp/")[1] |
||||
|
account = account_start.split("?")[0] |
||||
|
|
||||
|
digits_start = totp_uri.split("&digits=")[1] |
||||
|
digits = digits_start.split("&")[0] |
||||
|
|
||||
|
issuer = urllib.parse.unquote_plus(issuer) |
||||
|
secret = urllib.parse.unquote_plus(secret) |
||||
|
algorithm = urllib.parse.unquote_plus(algorithm) |
||||
|
account = urllib.parse.unquote_plus(account) |
||||
|
digits = urllib.parse.unquote_plus(digits) |
||||
|
|
||||
|
return {"issuer": issuer, "account": account, "algorithm": algorithm, "secret": secret, "digits": digits} |
||||
|
|
||||
|
def generate_qr_code(totp_details: dict): |
||||
|
issuer = urllib.parse.quote(totp_details["issuer"]) |
||||
|
account = urllib.parse.quote(totp_details["account"]) |
||||
|
algorithm = urllib.parse.quote(totp_details["algorithm"]) |
||||
|
secret = urllib.parse.quote(totp_details["secret"]) |
||||
|
digits = urllib.parse.quote(totp_details["digits"]) |
||||
|
|
||||
|
generated_uri = "otpauth://totp/%s?algorithm=%s&digits=%s&issuer=%s&secret=%s" % ( |
||||
|
account, |
||||
|
algorithm, |
||||
|
digits, |
||||
|
issuer, |
||||
|
secret |
||||
|
) |
||||
|
img = qrcode.make(generated_uri) |
||||
|
print(generated_uri) |
||||
|
img.show() |
||||
|
|
||||
|
|
||||
|
def main(): |
||||
|
print("Grabbing screenshot in 2 seconds...") |
||||
|
time.sleep(2) |
||||
|
decoded_data = decode_screenshot_img() |
||||
|
print(decoded_data) |
||||
|
|
||||
|
totp_uri_list: list = [] |
||||
|
if "otpauth-migration" in decoded_data: |
||||
|
totp_uri_list = otpauth_migration_decoder.src.decoder.start_decode_migration(decoded_data) |
||||
|
elif "otpauth://totp/" in decoded_data: |
||||
|
totp_uri_list.append(decoded_data) |
||||
|
else: |
||||
|
print("Error in QR code. Please check if it is proper Google Authenticator QR code.") |
||||
|
return |
||||
|
|
||||
|
for uri in totp_uri_list: |
||||
|
try: |
||||
|
curr_totp_details = decode_totp(uri) |
||||
|
print(curr_totp_details) |
||||
|
except: |
||||
|
print("Error in parsing the following URI: %s" % uri) |
||||
|
|
||||
|
# Checking for each item |
||||
|
print() |
||||
|
print() |
||||
|
print("Do you want to change: (separator is \":::\")") |
||||
|
print("%s:::%s ... " % (curr_totp_details["issuer"], curr_totp_details["account"]), end="") |
||||
|
updated_data = input() |
||||
|
updated_data_splitted = updated_data.split(":::") |
||||
|
if updated_data != "": |
||||
|
curr_totp_details["issuer"] = updated_data_splitted[0] |
||||
|
curr_totp_details["account"] = updated_data_splitted[1] |
||||
|
generate_qr_code(curr_totp_details) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
@ -0,0 +1,11 @@ |
|||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
indent_style = space |
||||
|
indent_size = 4 |
||||
|
charset = utf-8 |
||||
|
trim_trailing_whitespace = true |
||||
|
insert_final_newline = true |
||||
|
|
||||
|
[*.{json,yml}] |
||||
|
indent_size = 2 |
@ -0,0 +1 @@ |
|||||
|
layout python python3.9 |
@ -0,0 +1,2 @@ |
|||||
|
decoder.build/ |
||||
|
decoder.dist/ |
@ -0,0 +1,31 @@ |
|||||
|
# otpauth migration decoder |
||||
|
|
||||
|
Convert Google Authenticator data to plain otpauth links |
||||
|
|
||||
|
|
||||
|
## usage |
||||
|
|
||||
|
1. get QR code in "Google Authenticator" app (Menu → Transfer accounts → Export accounts → Select accounts → Next) |
||||
|
1. extract link from QR code with your preferred [QR codes reading software](https://play.google.com/store/search?q=qr%20code%20reader) |
||||
|
1. pass migration link (`otpauth-migration://offline?data=...`) to this tool |
||||
|
|
||||
|
## requirements |
||||
|
|
||||
|
The protobuf package is required to running this script: |
||||
|
|
||||
|
```.shell |
||||
|
$ pip install protobuf |
||||
|
``` |
||||
|
|
||||
|
## example |
||||
|
|
||||
|
``` |
||||
|
$ python decoder.py "otpauth-migration://offline?data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC" |
||||
|
``` |
||||
|
|
||||
|
|
||||
|
## references |
||||
|
|
||||
|
1. [otpauth:// URI format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) |
||||
|
1. [Protocol Buffer Basics: Python](https://developers.google.com/protocol-buffers/docs/pythontutorial) |
||||
|
1. [Authenticator live demo](https://rootprojects.org/authenticator/) |
@ -0,0 +1,526 @@ |
|||||
|
[[package]] |
||||
|
name = "appdirs" |
||||
|
version = "1.4.4" |
||||
|
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "atomicwrites" |
||||
|
version = "1.4.0" |
||||
|
description = "Atomic file writes." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "attrs" |
||||
|
version = "21.2.0" |
||||
|
description = "Classes Without Boilerplate" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" |
||||
|
|
||||
|
[package.extras] |
||||
|
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] |
||||
|
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] |
||||
|
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] |
||||
|
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "click" |
||||
|
version = "8.0.1" |
||||
|
description = "Composable command line interface toolkit" |
||||
|
category = "main" |
||||
|
optional = false |
||||
|
python-versions = ">=3.6" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
colorama = {version = "*", markers = "platform_system == \"Windows\""} |
||||
|
|
||||
|
[[package]] |
||||
|
name = "colorama" |
||||
|
version = "0.4.4" |
||||
|
description = "Cross-platform colored terminal text." |
||||
|
category = "main" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "coverage" |
||||
|
version = "5.5" |
||||
|
description = "Code coverage measurement for Python" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" |
||||
|
|
||||
|
[package.extras] |
||||
|
toml = ["toml"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "distlib" |
||||
|
version = "0.3.2" |
||||
|
description = "Distribution utilities" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "filelock" |
||||
|
version = "3.0.12" |
||||
|
description = "A platform independent file lock." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "iniconfig" |
||||
|
version = "1.1.1" |
||||
|
description = "iniconfig: brain-dead simple config-ini parsing" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "isort" |
||||
|
version = "5.9.1" |
||||
|
description = "A Python utility / library to sort Python imports." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=3.6.1,<4.0" |
||||
|
|
||||
|
[package.extras] |
||||
|
pipfile_deprecated_finder = ["pipreqs", "requirementslib"] |
||||
|
requirements_deprecated_finder = ["pipreqs", "pip-api"] |
||||
|
colors = ["colorama (>=0.4.3,<0.5.0)"] |
||||
|
plugins = ["setuptools"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "mypy" |
||||
|
version = "0.910" |
||||
|
description = "Optional static typing for Python" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=3.5" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
mypy-extensions = ">=0.4.3,<0.5.0" |
||||
|
toml = "*" |
||||
|
typing-extensions = ">=3.7.4" |
||||
|
|
||||
|
[package.extras] |
||||
|
dmypy = ["psutil (>=4.0)"] |
||||
|
python2 = ["typed-ast (>=1.4.0,<1.5.0)"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "mypy-extensions" |
||||
|
version = "0.4.3" |
||||
|
description = "Experimental type system extensions for programs checked with the mypy typechecker." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "nuitka" |
||||
|
version = "0.6.16" |
||||
|
description = "Python compiler with full language support and CPython compatibility" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "packaging" |
||||
|
version = "20.9" |
||||
|
description = "Core utilities for Python packages" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
pyparsing = ">=2.0.2" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "pluggy" |
||||
|
version = "0.13.1" |
||||
|
description = "plugin and hook calling mechanisms for python" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||
|
|
||||
|
[package.extras] |
||||
|
dev = ["pre-commit", "tox"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "protobuf" |
||||
|
version = "3.17.3" |
||||
|
description = "Protocol Buffers" |
||||
|
category = "main" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
six = ">=1.9" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "py" |
||||
|
version = "1.10.0" |
||||
|
description = "library with cross-python path, ini-parsing, io, code, log facilities" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "pyparsing" |
||||
|
version = "2.4.7" |
||||
|
description = "Python parsing module" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "pytest" |
||||
|
version = "6.2.4" |
||||
|
description = "pytest: simple powerful testing with Python" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=3.6" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} |
||||
|
attrs = ">=19.2.0" |
||||
|
colorama = {version = "*", markers = "sys_platform == \"win32\""} |
||||
|
iniconfig = "*" |
||||
|
packaging = "*" |
||||
|
pluggy = ">=0.12,<1.0.0a1" |
||||
|
py = ">=1.8.2" |
||||
|
toml = "*" |
||||
|
|
||||
|
[package.extras] |
||||
|
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "pytest-cov" |
||||
|
version = "2.12.1" |
||||
|
description = "Pytest plugin for measuring coverage." |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
coverage = ">=5.2.1" |
||||
|
pytest = ">=4.6" |
||||
|
toml = "*" |
||||
|
|
||||
|
[package.extras] |
||||
|
testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "six" |
||||
|
version = "1.16.0" |
||||
|
description = "Python 2 and 3 compatibility utilities" |
||||
|
category = "main" |
||||
|
optional = false |
||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "toml" |
||||
|
version = "0.10.2" |
||||
|
description = "Python Library for Tom's Obvious, Minimal Language" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "tox" |
||||
|
version = "3.23.1" |
||||
|
description = "tox is a generic virtualenv management and test command line tool" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} |
||||
|
filelock = ">=3.0.0" |
||||
|
packaging = ">=14" |
||||
|
pluggy = ">=0.12.0" |
||||
|
py = ">=1.4.17" |
||||
|
six = ">=1.14.0" |
||||
|
toml = ">=0.9.4" |
||||
|
virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" |
||||
|
|
||||
|
[package.extras] |
||||
|
docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] |
||||
|
testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)", "pytest-xdist (>=1.22.2)", "pathlib2 (>=2.3.3)"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "tox-poetry" |
||||
|
version = "0.4.0" |
||||
|
description = "Tox poetry plugin" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
pluggy = "*" |
||||
|
toml = "*" |
||||
|
tox = {version = ">=3.7.0", markers = "python_version >= \"3\""} |
||||
|
|
||||
|
[package.extras] |
||||
|
test = ["coverage", "pytest", "pycodestyle", "pylint"] |
||||
|
|
||||
|
[[package]] |
||||
|
name = "types-futures" |
||||
|
version = "0.1.6" |
||||
|
description = "Typing stubs for futures" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "types-protobuf" |
||||
|
version = "0.1.13" |
||||
|
description = "Typing stubs for protobuf" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
types-futures = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "typing-extensions" |
||||
|
version = "3.10.0.0" |
||||
|
description = "Backported and Experimental Type Hints for Python 3.5+" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "*" |
||||
|
|
||||
|
[[package]] |
||||
|
name = "virtualenv" |
||||
|
version = "20.4.7" |
||||
|
description = "Virtual Python Environment builder" |
||||
|
category = "dev" |
||||
|
optional = false |
||||
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" |
||||
|
|
||||
|
[package.dependencies] |
||||
|
appdirs = ">=1.4.3,<2" |
||||
|
distlib = ">=0.3.1,<1" |
||||
|
filelock = ">=3.0.0,<4" |
||||
|
six = ">=1.9.0,<2" |
||||
|
|
||||
|
[package.extras] |
||||
|
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] |
||||
|
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] |
||||
|
|
||||
|
[metadata] |
||||
|
lock-version = "1.1" |
||||
|
python-versions = "^3.9" |
||||
|
content-hash = "55529582d8d2d5c532c77d89b7d15fde600d866bfc8f9651d1d44a69e328e501" |
||||
|
|
||||
|
[metadata.files] |
||||
|
appdirs = [ |
||||
|
{file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, |
||||
|
{file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, |
||||
|
] |
||||
|
atomicwrites = [ |
||||
|
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, |
||||
|
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, |
||||
|
] |
||||
|
attrs = [ |
||||
|
{file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, |
||||
|
{file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, |
||||
|
] |
||||
|
click = [ |
||||
|
{file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, |
||||
|
{file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, |
||||
|
] |
||||
|
colorama = [ |
||||
|
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, |
||||
|
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, |
||||
|
] |
||||
|
coverage = [ |
||||
|
{file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, |
||||
|
{file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, |
||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, |
||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, |
||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, |
||||
|
{file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, |
||||
|
{file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, |
||||
|
{file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, |
||||
|
{file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, |
||||
|
{file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, |
||||
|
{file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, |
||||
|
{file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, |
||||
|
{file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, |
||||
|
{file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, |
||||
|
{file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, |
||||
|
{file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, |
||||
|
{file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, |
||||
|
] |
||||
|
distlib = [ |
||||
|
{file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"}, |
||||
|
{file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"}, |
||||
|
] |
||||
|
filelock = [ |
||||
|
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, |
||||
|
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, |
||||
|
] |
||||
|
iniconfig = [ |
||||
|
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, |
||||
|
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, |
||||
|
] |
||||
|
isort = [ |
||||
|
{file = "isort-5.9.1-py3-none-any.whl", hash = "sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"}, |
||||
|
{file = "isort-5.9.1.tar.gz", hash = "sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56"}, |
||||
|
] |
||||
|
mypy = [ |
||||
|
{file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, |
||||
|
{file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, |
||||
|
{file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, |
||||
|
{file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, |
||||
|
{file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, |
||||
|
{file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, |
||||
|
{file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, |
||||
|
{file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, |
||||
|
{file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, |
||||
|
{file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, |
||||
|
{file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, |
||||
|
{file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, |
||||
|
{file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, |
||||
|
{file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, |
||||
|
{file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, |
||||
|
{file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, |
||||
|
{file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, |
||||
|
{file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, |
||||
|
{file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, |
||||
|
{file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, |
||||
|
{file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, |
||||
|
{file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, |
||||
|
{file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, |
||||
|
] |
||||
|
mypy-extensions = [ |
||||
|
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, |
||||
|
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, |
||||
|
] |
||||
|
nuitka = [ |
||||
|
{file = "Nuitka-0.6.16.tar.gz", hash = "sha256:f194af0eecacda5b44c7bd26a29ddf3a9165dfda02731466ce17ed1ebd151b8b"}, |
||||
|
] |
||||
|
packaging = [ |
||||
|
{file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, |
||||
|
{file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, |
||||
|
] |
||||
|
pluggy = [ |
||||
|
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, |
||||
|
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, |
||||
|
] |
||||
|
protobuf = [ |
||||
|
{file = "protobuf-3.17.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ab6bb0e270c6c58e7ff4345b3a803cc59dbee19ddf77a4719c5b635f1d547aa8"}, |
||||
|
{file = "protobuf-3.17.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:13ee7be3c2d9a5d2b42a1030976f760f28755fcf5863c55b1460fd205e6cd637"}, |
||||
|
{file = "protobuf-3.17.3-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:1556a1049ccec58c7855a78d27e5c6e70e95103b32de9142bae0576e9200a1b0"}, |
||||
|
{file = "protobuf-3.17.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f0e59430ee953184a703a324b8ec52f571c6c4259d496a19d1cabcdc19dabc62"}, |
||||
|
{file = "protobuf-3.17.3-cp35-cp35m-win32.whl", hash = "sha256:a981222367fb4210a10a929ad5983ae93bd5a050a0824fc35d6371c07b78caf6"}, |
||||
|
{file = "protobuf-3.17.3-cp35-cp35m-win_amd64.whl", hash = "sha256:6d847c59963c03fd7a0cd7c488cadfa10cda4fff34d8bc8cba92935a91b7a037"}, |
||||
|
{file = "protobuf-3.17.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:145ce0af55c4259ca74993ddab3479c78af064002ec8227beb3d944405123c71"}, |
||||
|
{file = "protobuf-3.17.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ce4d8bf0321e7b2d4395e253f8002a1a5ffbcfd7bcc0a6ba46712c07d47d0b4"}, |
||||
|
{file = "protobuf-3.17.3-cp36-cp36m-win32.whl", hash = "sha256:7a4c97961e9e5b03a56f9a6c82742ed55375c4a25f2692b625d4087d02ed31b9"}, |
||||
|
{file = "protobuf-3.17.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a22b3a0dbac6544dacbafd4c5f6a29e389a50e3b193e2c70dae6bbf7930f651d"}, |
||||
|
{file = "protobuf-3.17.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ffea251f5cd3c0b9b43c7a7a912777e0bc86263436a87c2555242a348817221b"}, |
||||
|
{file = "protobuf-3.17.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:9b7a5c1022e0fa0dbde7fd03682d07d14624ad870ae52054849d8960f04bc764"}, |
||||
|
{file = "protobuf-3.17.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8727ee027157516e2c311f218ebf2260a18088ffb2d29473e82add217d196b1c"}, |
||||
|
{file = "protobuf-3.17.3-cp37-cp37m-win32.whl", hash = "sha256:14c1c9377a7ffbeaccd4722ab0aa900091f52b516ad89c4b0c3bb0a4af903ba5"}, |
||||
|
{file = "protobuf-3.17.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c56c050a947186ba51de4f94ab441d7f04fcd44c56df6e922369cc2e1a92d683"}, |
||||
|
{file = "protobuf-3.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2ae692bb6d1992afb6b74348e7bb648a75bb0d3565a3f5eea5bec8f62bd06d87"}, |
||||
|
{file = "protobuf-3.17.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:99938f2a2d7ca6563c0ade0c5ca8982264c484fdecf418bd68e880a7ab5730b1"}, |
||||
|
{file = "protobuf-3.17.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6902a1e4b7a319ec611a7345ff81b6b004b36b0d2196ce7a748b3493da3d226d"}, |
||||
|
{file = "protobuf-3.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4ffbd23640bb7403574f7aff8368e2aeb2ec9a5c6306580be48ac59a6bac8bde"}, |
||||
|
{file = "protobuf-3.17.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:26010f693b675ff5a1d0e1bdb17689b8b716a18709113288fead438703d45539"}, |
||||
|
{file = "protobuf-3.17.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76d9686e088fece2450dbc7ee905f9be904e427341d289acbe9ad00b78ebd47"}, |
||||
|
{file = "protobuf-3.17.3-py2.py3-none-any.whl", hash = "sha256:2bfb815216a9cd9faec52b16fd2bfa68437a44b67c56bee59bc3926522ecb04e"}, |
||||
|
{file = "protobuf-3.17.3.tar.gz", hash = "sha256:72804ea5eaa9c22a090d2803813e280fb273b62d5ae497aaf3553d141c4fdd7b"}, |
||||
|
] |
||||
|
py = [ |
||||
|
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, |
||||
|
{file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, |
||||
|
] |
||||
|
pyparsing = [ |
||||
|
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, |
||||
|
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, |
||||
|
] |
||||
|
pytest = [ |
||||
|
{file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, |
||||
|
{file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, |
||||
|
] |
||||
|
pytest-cov = [ |
||||
|
{file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, |
||||
|
{file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, |
||||
|
] |
||||
|
six = [ |
||||
|
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, |
||||
|
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, |
||||
|
] |
||||
|
toml = [ |
||||
|
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, |
||||
|
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, |
||||
|
] |
||||
|
tox = [ |
||||
|
{file = "tox-3.23.1-py2.py3-none-any.whl", hash = "sha256:b0b5818049a1c1997599d42012a637a33f24c62ab8187223fdd318fa8522637b"}, |
||||
|
{file = "tox-3.23.1.tar.gz", hash = "sha256:307a81ddb82bd463971a273f33e9533a24ed22185f27db8ce3386bff27d324e3"}, |
||||
|
] |
||||
|
tox-poetry = [ |
||||
|
{file = "tox-poetry-0.4.0.tar.gz", hash = "sha256:b926723cb1dea87902299dd8cad458d0b80ab7345bbf7983e6dab1cbf951090d"}, |
||||
|
{file = "tox_poetry-0.4.0-py2.py3-none-any.whl", hash = "sha256:b529f3b534b351b7cf9506caa66e2f230dded38b2b74245559c3d662685f4494"}, |
||||
|
] |
||||
|
types-futures = [ |
||||
|
{file = "types-futures-0.1.6.tar.gz", hash = "sha256:da372dd55dc08c257de1e3dfd56273e44af9668e077047b0509adcfc43dd2838"}, |
||||
|
{file = "types_futures-0.1.6-py3-none-any.whl", hash = "sha256:7cb32c3fb4885089d78873a28ad33db3d5300661eac8b3ad327f4f5616fdf742"}, |
||||
|
] |
||||
|
types-protobuf = [ |
||||
|
{file = "types-protobuf-0.1.13.tar.gz", hash = "sha256:1c5800d7b6bb5ccf27d5256903fd32d96745a54b3a32179fc1606e8179a035e9"}, |
||||
|
{file = "types_protobuf-0.1.13-py2.py3-none-any.whl", hash = "sha256:771c272572be5396a1946f035db730d8d6be03ad21ff6885e0e7d1b18549af89"}, |
||||
|
] |
||||
|
typing-extensions = [ |
||||
|
{file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, |
||||
|
{file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, |
||||
|
{file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, |
||||
|
] |
||||
|
virtualenv = [ |
||||
|
{file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"}, |
||||
|
{file = "virtualenv-20.4.7.tar.gz", hash = "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467"}, |
||||
|
] |
@ -0,0 +1,24 @@ |
|||||
|
[tool.poetry] |
||||
|
name = "otpauth-migration-decoder" |
||||
|
version = "0.1.1" |
||||
|
description = "Convert otpauth-migration to plain link" |
||||
|
authors = ["digitalduke <digitalduke@gmail.com>"] |
||||
|
|
||||
|
[tool.poetry.dependencies] |
||||
|
python = "^3.9" |
||||
|
protobuf = "^3.17.3" |
||||
|
click = "^8.0.1" |
||||
|
|
||||
|
[tool.poetry.dev-dependencies] |
||||
|
Nuitka = "^0.6.15" |
||||
|
isort = "^5.9.1" |
||||
|
pytest = "*" |
||||
|
pytest-cov = "*" |
||||
|
tox = "*" |
||||
|
tox-poetry = "*" |
||||
|
mypy = "^0.910" |
||||
|
types-protobuf = "*" |
||||
|
|
||||
|
[build-system] |
||||
|
requires = ["poetry-core>=1.0.0"] |
||||
|
build-backend = "poetry.core.masonry.api" |
@ -0,0 +1,12 @@ |
|||||
|
[tool:isort] |
||||
|
line_length = 120 |
||||
|
indent = ' ' |
||||
|
multi_line_output = 3 |
||||
|
no_lines_before = LOCALFOLDER |
||||
|
use_parentheses = true |
||||
|
lines_after_imports = 2 |
||||
|
skip_glob = .tox,.direnv,*_pb2.py |
||||
|
include_trailing_comma = true |
||||
|
case_sensitive = true |
||||
|
force_grid_wrap = 2 |
||||
|
combine_as_imports = true |
@ -0,0 +1,109 @@ |
|||||
|
from base64 import ( |
||||
|
b32encode, |
||||
|
b64decode, |
||||
|
) |
||||
|
from collections.abc import Generator |
||||
|
from typing import ( |
||||
|
Any, |
||||
|
Dict, |
||||
|
List, |
||||
|
Union, |
||||
|
) |
||||
|
from urllib.parse import ( |
||||
|
ParseResult, |
||||
|
parse_qs, |
||||
|
quote, |
||||
|
urlencode, |
||||
|
urlparse, |
||||
|
) |
||||
|
|
||||
|
from .otpauth_enums import ( |
||||
|
Algorithm, |
||||
|
DigitCount, |
||||
|
OtpType, |
||||
|
) |
||||
|
from .otpauth_migration_pb2 import Payload |
||||
|
|
||||
|
|
||||
|
SCHEME = 'otpauth-migration' |
||||
|
HOSTNAME = 'offline' |
||||
|
PAYLOAD_MARK = 'data' |
||||
|
EXAMPLE_PAYLOAD = 'CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC' |
||||
|
EXAMPLE_MIGRATION = f'{SCHEME}://{HOSTNAME}?{PAYLOAD_MARK}={EXAMPLE_PAYLOAD}' |
||||
|
|
||||
|
|
||||
|
def is_migration_incorrect( |
||||
|
*, |
||||
|
parsed_url: ParseResult, |
||||
|
parsed_qs: Dict[str, Any], |
||||
|
) -> bool: |
||||
|
return ( |
||||
|
parsed_url.scheme != SCHEME |
||||
|
or parsed_url.hostname != HOSTNAME |
||||
|
or PAYLOAD_MARK not in parsed_qs |
||||
|
or not isinstance(parsed_qs[PAYLOAD_MARK], list) |
||||
|
) |
||||
|
|
||||
|
|
||||
|
def decoded_data(data: List[str]) -> Generator: |
||||
|
for data_item in data: |
||||
|
yield b64decode(data_item) |
||||
|
|
||||
|
|
||||
|
def decode_secret(secret: bytes) -> str: |
||||
|
return str(b32encode(secret), 'utf-8').replace('=', '') |
||||
|
|
||||
|
|
||||
|
def get_url_params(otp: Payload.OtpParameters) -> str: |
||||
|
params: dict[str, Union[str, int]] = {} |
||||
|
|
||||
|
if otp.algorithm: |
||||
|
params.update(algorithm=Algorithm.get(otp.algorithm, '')) |
||||
|
if otp.digits: |
||||
|
params.update(digits=DigitCount.get(otp.digits, '')) |
||||
|
if otp.issuer: |
||||
|
params.update(issuer=otp.issuer) |
||||
|
if otp.secret: |
||||
|
otp_secret = decode_secret(otp.secret) |
||||
|
params.update(secret=otp_secret) |
||||
|
|
||||
|
return urlencode(params) |
||||
|
|
||||
|
|
||||
|
def get_otpauth_url(otp: Payload.OtpParameters) -> str: |
||||
|
otp_type = OtpType.get(otp.type, '') |
||||
|
otp_name = quote(otp.name) |
||||
|
otp_params = get_url_params(otp) |
||||
|
|
||||
|
return f'otpauth://{otp_type}/{otp_name}?{otp_params}' |
||||
|
|
||||
|
|
||||
|
def validate_migration(migration: str) -> list: |
||||
|
url: ParseResult = urlparse(migration) |
||||
|
qs: Dict[str, Any] = parse_qs(url.query) |
||||
|
|
||||
|
|
||||
|
return qs[PAYLOAD_MARK] |
||||
|
|
||||
|
|
||||
|
def decoder(migration_data: list) -> list: |
||||
|
"""Convert Google Authenticator data to plain otpauth links""" |
||||
|
result = [] |
||||
|
|
||||
|
for payload in decoded_data(data=migration_data): |
||||
|
migration_payload = Payload() |
||||
|
migration_payload.ParseFromString(payload) |
||||
|
|
||||
|
for otp_item in migration_payload.otp_parameters: |
||||
|
result.append(get_otpauth_url(otp_item)) |
||||
|
|
||||
|
return result |
||||
|
|
||||
|
def start_decode_migration(migration_code: str) -> list: |
||||
|
payload_list = validate_migration(migration_code) |
||||
|
results = decoder(payload_list) |
||||
|
return results |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
start_decode_migration("otpauth-migration://offline?data=CkAKFFUlyp7lQLXJnL8Oc5%2BjVRVCEIMPEhpHb29nbGU6cHVha2Foa2luQGdtYWlsLmNvbRoGR29vZ2xlIAEoATACCj0KFEMSL8awHj%2BEFzX2cODi9rNgfvPFEhdHb29nbGU6a2Foa2luQGdtYWlsLmNvbRoGR29vZ2xlIAEoATACCi8KEBdHlsAmppdFyWD18FyPcQkSDXNhbXVlbEBhcG9sbG8aBmFwb2xsbyABKAEwAgouChQpi9Btg%2BHoQJj6tilYEsOef%2BSkiRIQc2FtdWVsQGFwaHJvZGl0ZSABKAEwAgo1ChDnUjGxj%2BJmsJJxdcrbBNpqEhBzYW11ZWxAc2FtdWVscHVhGglzYW11ZWxwdWEgASgBMAIKUAoUihAclMtkwYi3NfvbkHcHmgAi2yESJmdpdGxhYi5jb206Z2l0bGFiLmNvbV9rYWhraW5AZ21haWwuY29tGgpnaXRsYWIuY29tIAEoATACCioKCsaExzWrYHtcsLoSDkdpdEh1Yjp0ZWxib29uGgZHaXRIdWIgASgBMAIKbwooyfRfWMXsY%2B%2B%2FVvZKl9ypsl1VLAZrOPmyr3AsGxeCOuTHmGdGpt4OyBIoQW1hem9uIFdlYiBTZXJ2aWNlczp0ZWxib29uQDI2Nzk5NDAyOTgxMBoTQW1hem9uIFdlYiBTZXJ2aWNlcyABKAEwAgp%2FCiieutAtJJdKrtOMVznsVaMbvrpM%2FNvwURQSChMpPzgI2PlVshsoRk3QEjhBbWF6b24gV2ViIFNlcnZpY2VzOnJvb3QtYWNjb3VudC1tZmEtZGV2aWNlQDI2Nzk5NDAyOTgxMBoTQW1hem9uIFdlYiBTZXJ2aWNlcyABKAEwAgozCgr2b4%2BKdfJzdo%2ByEhRzYW11ZWxAd2F0Y2h0b3dyLmNvbRoJTWljcm9zb2Z0IAEoATACEAEYAiAAKKessZb6%2F%2F%2F%2F%2FwE%3D") |
||||
|
|
@ -0,0 +1,37 @@ |
|||||
|
syntax = "proto3"; |
||||
|
|
||||
|
package otpauth_migration; |
||||
|
|
||||
|
message Payload { |
||||
|
enum Algorithm { |
||||
|
ALGORITHM_UNSPECIFIED = 0; |
||||
|
ALGORITHM_SHA1 = 1; |
||||
|
ALGORITHM_SHA256 = 2; |
||||
|
ALGORITHM_SHA512 = 3; |
||||
|
ALGORITHM_MD5 = 4; |
||||
|
} |
||||
|
enum DigitCount { |
||||
|
DIGIT_COUNT_UNSPECIFIED = 0; |
||||
|
DIGIT_COUNT_SIX = 1; |
||||
|
DIGIT_COUNT_EIGHT = 2; |
||||
|
} |
||||
|
enum OtpType { |
||||
|
OTP_TYPE_UNSPECIFIED = 0; |
||||
|
OTP_TYPE_HOTP = 1; |
||||
|
OTP_TYPE_TOTP = 2; |
||||
|
} |
||||
|
message OtpParameters { |
||||
|
bytes secret = 1; |
||||
|
string name = 2; |
||||
|
string issuer = 3; |
||||
|
Algorithm algorithm = 4; |
||||
|
DigitCount digits = 5; |
||||
|
OtpType type = 6; |
||||
|
int64 counter = 7; |
||||
|
} |
||||
|
repeated OtpParameters otp_parameters = 1; |
||||
|
int32 version = 2; |
||||
|
int32 batch_size = 3; |
||||
|
int32 batch_index = 4; |
||||
|
int32 batch_id = 5; |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
from typing import Mapping |
||||
|
|
||||
|
|
||||
|
Algorithm: Mapping[int, str] = { |
||||
|
1: 'SHA1', |
||||
|
2: 'SHA256', |
||||
|
3: 'SHA512', |
||||
|
4: 'MD5', |
||||
|
} |
||||
|
|
||||
|
DigitCount: Mapping[int, str] = { |
||||
|
1: '6', |
||||
|
2: '8', |
||||
|
} |
||||
|
|
||||
|
OtpType: Mapping[int, str] = { |
||||
|
1: 'hotp', |
||||
|
2: 'totp', |
||||
|
} |
@ -0,0 +1,290 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Generated by the protocol buffer compiler. DO NOT EDIT! |
||||
|
# source: otpauth-migration.proto |
||||
|
"""Generated protocol buffer code.""" |
||||
|
from google.protobuf import descriptor as _descriptor |
||||
|
from google.protobuf import message as _message |
||||
|
from google.protobuf import reflection as _reflection |
||||
|
from google.protobuf import symbol_database as _symbol_database |
||||
|
# @@protoc_insertion_point(imports) |
||||
|
|
||||
|
_sym_db = _symbol_database.Default() |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
DESCRIPTOR = _descriptor.FileDescriptor( |
||||
|
name='otpauth-migration.proto', |
||||
|
package='otpauth_migration', |
||||
|
syntax='proto3', |
||||
|
serialized_options=None, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
serialized_pb=b'\n\x17otpauth-migration.proto\x12\x11otpauth_migration\"\xa7\x05\n\x07Payload\x12@\n\x0eotp_parameters\x18\x01 \x03(\x0b\x32(.otpauth_migration.Payload.OtpParameters\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x12\n\nbatch_size\x18\x03 \x01(\x05\x12\x13\n\x0b\x62\x61tch_index\x18\x04 \x01(\x05\x12\x10\n\x08\x62\x61tch_id\x18\x05 \x01(\x05\x1a\xf0\x01\n\rOtpParameters\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06issuer\x18\x03 \x01(\t\x12\x37\n\talgorithm\x18\x04 \x01(\x0e\x32$.otpauth_migration.Payload.Algorithm\x12\x35\n\x06\x64igits\x18\x05 \x01(\x0e\x32%.otpauth_migration.Payload.DigitCount\x12\x30\n\x04type\x18\x06 \x01(\x0e\x32\".otpauth_migration.Payload.OtpType\x12\x0f\n\x07\x63ounter\x18\x07 \x01(\x03\"y\n\tAlgorithm\x12\x19\n\x15\x41LGORITHM_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x41LGORITHM_SHA1\x10\x01\x12\x14\n\x10\x41LGORITHM_SHA256\x10\x02\x12\x14\n\x10\x41LGORITHM_SHA512\x10\x03\x12\x11\n\rALGORITHM_MD5\x10\x04\"U\n\nDigitCount\x12\x1b\n\x17\x44IGIT_COUNT_UNSPECIFIED\x10\x00\x12\x13\n\x0f\x44IGIT_COUNT_SIX\x10\x01\x12\x15\n\x11\x44IGIT_COUNT_EIGHT\x10\x02\"I\n\x07OtpType\x12\x18\n\x14OTP_TYPE_UNSPECIFIED\x10\x00\x12\x11\n\rOTP_TYPE_HOTP\x10\x01\x12\x11\n\rOTP_TYPE_TOTP\x10\x02\x62\x06proto3' |
||||
|
) |
||||
|
|
||||
|
|
||||
|
|
||||
|
_PAYLOAD_ALGORITHM = _descriptor.EnumDescriptor( |
||||
|
name='Algorithm', |
||||
|
full_name='otpauth_migration.Payload.Algorithm', |
||||
|
filename=None, |
||||
|
file=DESCRIPTOR, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
values=[ |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='ALGORITHM_UNSPECIFIED', index=0, number=0, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='ALGORITHM_SHA1', index=1, number=1, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='ALGORITHM_SHA256', index=2, number=2, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='ALGORITHM_SHA512', index=3, number=3, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='ALGORITHM_MD5', index=4, number=4, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
], |
||||
|
containing_type=None, |
||||
|
serialized_options=None, |
||||
|
serialized_start=447, |
||||
|
serialized_end=568, |
||||
|
) |
||||
|
_sym_db.RegisterEnumDescriptor(_PAYLOAD_ALGORITHM) |
||||
|
|
||||
|
_PAYLOAD_DIGITCOUNT = _descriptor.EnumDescriptor( |
||||
|
name='DigitCount', |
||||
|
full_name='otpauth_migration.Payload.DigitCount', |
||||
|
filename=None, |
||||
|
file=DESCRIPTOR, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
values=[ |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='DIGIT_COUNT_UNSPECIFIED', index=0, number=0, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='DIGIT_COUNT_SIX', index=1, number=1, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='DIGIT_COUNT_EIGHT', index=2, number=2, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
], |
||||
|
containing_type=None, |
||||
|
serialized_options=None, |
||||
|
serialized_start=570, |
||||
|
serialized_end=655, |
||||
|
) |
||||
|
_sym_db.RegisterEnumDescriptor(_PAYLOAD_DIGITCOUNT) |
||||
|
|
||||
|
_PAYLOAD_OTPTYPE = _descriptor.EnumDescriptor( |
||||
|
name='OtpType', |
||||
|
full_name='otpauth_migration.Payload.OtpType', |
||||
|
filename=None, |
||||
|
file=DESCRIPTOR, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
values=[ |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='OTP_TYPE_UNSPECIFIED', index=0, number=0, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='OTP_TYPE_HOTP', index=1, number=1, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.EnumValueDescriptor( |
||||
|
name='OTP_TYPE_TOTP', index=2, number=2, |
||||
|
serialized_options=None, |
||||
|
type=None, |
||||
|
create_key=_descriptor._internal_create_key), |
||||
|
], |
||||
|
containing_type=None, |
||||
|
serialized_options=None, |
||||
|
serialized_start=657, |
||||
|
serialized_end=730, |
||||
|
) |
||||
|
_sym_db.RegisterEnumDescriptor(_PAYLOAD_OTPTYPE) |
||||
|
|
||||
|
|
||||
|
_PAYLOAD_OTPPARAMETERS = _descriptor.Descriptor( |
||||
|
name='OtpParameters', |
||||
|
full_name='otpauth_migration.Payload.OtpParameters', |
||||
|
filename=None, |
||||
|
file=DESCRIPTOR, |
||||
|
containing_type=None, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
fields=[ |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='secret', full_name='otpauth_migration.Payload.OtpParameters.secret', index=0, |
||||
|
number=1, type=12, cpp_type=9, label=1, |
||||
|
has_default_value=False, default_value=b"", |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='name', full_name='otpauth_migration.Payload.OtpParameters.name', index=1, |
||||
|
number=2, type=9, cpp_type=9, label=1, |
||||
|
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='issuer', full_name='otpauth_migration.Payload.OtpParameters.issuer', index=2, |
||||
|
number=3, type=9, cpp_type=9, label=1, |
||||
|
has_default_value=False, default_value=b"".decode('utf-8'), |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='algorithm', full_name='otpauth_migration.Payload.OtpParameters.algorithm', index=3, |
||||
|
number=4, type=14, cpp_type=8, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='digits', full_name='otpauth_migration.Payload.OtpParameters.digits', index=4, |
||||
|
number=5, type=14, cpp_type=8, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='type', full_name='otpauth_migration.Payload.OtpParameters.type', index=5, |
||||
|
number=6, type=14, cpp_type=8, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='counter', full_name='otpauth_migration.Payload.OtpParameters.counter', index=6, |
||||
|
number=7, type=3, cpp_type=2, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
], |
||||
|
extensions=[ |
||||
|
], |
||||
|
nested_types=[], |
||||
|
enum_types=[ |
||||
|
], |
||||
|
serialized_options=None, |
||||
|
is_extendable=False, |
||||
|
syntax='proto3', |
||||
|
extension_ranges=[], |
||||
|
oneofs=[ |
||||
|
], |
||||
|
serialized_start=205, |
||||
|
serialized_end=445, |
||||
|
) |
||||
|
|
||||
|
_PAYLOAD = _descriptor.Descriptor( |
||||
|
name='Payload', |
||||
|
full_name='otpauth_migration.Payload', |
||||
|
filename=None, |
||||
|
file=DESCRIPTOR, |
||||
|
containing_type=None, |
||||
|
create_key=_descriptor._internal_create_key, |
||||
|
fields=[ |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='otp_parameters', full_name='otpauth_migration.Payload.otp_parameters', index=0, |
||||
|
number=1, type=11, cpp_type=10, label=3, |
||||
|
has_default_value=False, default_value=[], |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='version', full_name='otpauth_migration.Payload.version', index=1, |
||||
|
number=2, type=5, cpp_type=1, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='batch_size', full_name='otpauth_migration.Payload.batch_size', index=2, |
||||
|
number=3, type=5, cpp_type=1, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='batch_index', full_name='otpauth_migration.Payload.batch_index', index=3, |
||||
|
number=4, type=5, cpp_type=1, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
_descriptor.FieldDescriptor( |
||||
|
name='batch_id', full_name='otpauth_migration.Payload.batch_id', index=4, |
||||
|
number=5, type=5, cpp_type=1, label=1, |
||||
|
has_default_value=False, default_value=0, |
||||
|
message_type=None, enum_type=None, containing_type=None, |
||||
|
is_extension=False, extension_scope=None, |
||||
|
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), |
||||
|
], |
||||
|
extensions=[ |
||||
|
], |
||||
|
nested_types=[_PAYLOAD_OTPPARAMETERS, ], |
||||
|
enum_types=[ |
||||
|
_PAYLOAD_ALGORITHM, |
||||
|
_PAYLOAD_DIGITCOUNT, |
||||
|
_PAYLOAD_OTPTYPE, |
||||
|
], |
||||
|
serialized_options=None, |
||||
|
is_extendable=False, |
||||
|
syntax='proto3', |
||||
|
extension_ranges=[], |
||||
|
oneofs=[ |
||||
|
], |
||||
|
serialized_start=51, |
||||
|
serialized_end=730, |
||||
|
) |
||||
|
|
||||
|
_PAYLOAD_OTPPARAMETERS.fields_by_name['algorithm'].enum_type = _PAYLOAD_ALGORITHM |
||||
|
_PAYLOAD_OTPPARAMETERS.fields_by_name['digits'].enum_type = _PAYLOAD_DIGITCOUNT |
||||
|
_PAYLOAD_OTPPARAMETERS.fields_by_name['type'].enum_type = _PAYLOAD_OTPTYPE |
||||
|
_PAYLOAD_OTPPARAMETERS.containing_type = _PAYLOAD |
||||
|
_PAYLOAD.fields_by_name['otp_parameters'].message_type = _PAYLOAD_OTPPARAMETERS |
||||
|
_PAYLOAD_ALGORITHM.containing_type = _PAYLOAD |
||||
|
_PAYLOAD_DIGITCOUNT.containing_type = _PAYLOAD |
||||
|
_PAYLOAD_OTPTYPE.containing_type = _PAYLOAD |
||||
|
DESCRIPTOR.message_types_by_name['Payload'] = _PAYLOAD |
||||
|
_sym_db.RegisterFileDescriptor(DESCRIPTOR) |
||||
|
|
||||
|
Payload = _reflection.GeneratedProtocolMessageType('Payload', (_message.Message,), { |
||||
|
|
||||
|
'OtpParameters' : _reflection.GeneratedProtocolMessageType('OtpParameters', (_message.Message,), { |
||||
|
'DESCRIPTOR' : _PAYLOAD_OTPPARAMETERS, |
||||
|
'__module__' : 'otpauth_migration_pb2' |
||||
|
# @@protoc_insertion_point(class_scope:otpauth_migration.Payload.OtpParameters) |
||||
|
}) |
||||
|
, |
||||
|
'DESCRIPTOR' : _PAYLOAD, |
||||
|
'__module__' : 'otpauth_migration_pb2' |
||||
|
# @@protoc_insertion_point(class_scope:otpauth_migration.Payload) |
||||
|
}) |
||||
|
_sym_db.RegisterMessage(Payload) |
||||
|
_sym_db.RegisterMessage(Payload.OtpParameters) |
||||
|
|
||||
|
|
||||
|
# @@protoc_insertion_point(module_scope) |
@ -0,0 +1,111 @@ |
|||||
|
""" |
||||
|
@generated by mypy-protobuf. Do not edit manually! |
||||
|
isort:skip_file |
||||
|
""" |
||||
|
import builtins |
||||
|
import google.protobuf.descriptor |
||||
|
import google.protobuf.internal.containers |
||||
|
import google.protobuf.internal.enum_type_wrapper |
||||
|
import google.protobuf.message |
||||
|
import typing |
||||
|
import typing_extensions |
||||
|
|
||||
|
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor = ... |
||||
|
|
||||
|
class Payload(google.protobuf.message.Message): |
||||
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... |
||||
|
class Algorithm(metaclass=_Algorithm): |
||||
|
V = typing.NewType('V', builtins.int) |
||||
|
|
||||
|
ALGORITHM_UNSPECIFIED = Payload.Algorithm.V(0) |
||||
|
ALGORITHM_SHA1 = Payload.Algorithm.V(1) |
||||
|
ALGORITHM_SHA256 = Payload.Algorithm.V(2) |
||||
|
ALGORITHM_SHA512 = Payload.Algorithm.V(3) |
||||
|
ALGORITHM_MD5 = Payload.Algorithm.V(4) |
||||
|
|
||||
|
class _Algorithm(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[Algorithm.V], builtins.type): |
||||
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor = ... |
||||
|
ALGORITHM_UNSPECIFIED = Payload.Algorithm.V(0) |
||||
|
ALGORITHM_SHA1 = Payload.Algorithm.V(1) |
||||
|
ALGORITHM_SHA256 = Payload.Algorithm.V(2) |
||||
|
ALGORITHM_SHA512 = Payload.Algorithm.V(3) |
||||
|
ALGORITHM_MD5 = Payload.Algorithm.V(4) |
||||
|
|
||||
|
class DigitCount(metaclass=_DigitCount): |
||||
|
V = typing.NewType('V', builtins.int) |
||||
|
|
||||
|
DIGIT_COUNT_UNSPECIFIED = Payload.DigitCount.V(0) |
||||
|
DIGIT_COUNT_SIX = Payload.DigitCount.V(1) |
||||
|
DIGIT_COUNT_EIGHT = Payload.DigitCount.V(2) |
||||
|
|
||||
|
class _DigitCount(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DigitCount.V], builtins.type): |
||||
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor = ... |
||||
|
DIGIT_COUNT_UNSPECIFIED = Payload.DigitCount.V(0) |
||||
|
DIGIT_COUNT_SIX = Payload.DigitCount.V(1) |
||||
|
DIGIT_COUNT_EIGHT = Payload.DigitCount.V(2) |
||||
|
|
||||
|
class OtpType(metaclass=_OtpType): |
||||
|
V = typing.NewType('V', builtins.int) |
||||
|
|
||||
|
OTP_TYPE_UNSPECIFIED = Payload.OtpType.V(0) |
||||
|
OTP_TYPE_HOTP = Payload.OtpType.V(1) |
||||
|
OTP_TYPE_TOTP = Payload.OtpType.V(2) |
||||
|
|
||||
|
class _OtpType(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[OtpType.V], builtins.type): |
||||
|
DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor = ... |
||||
|
OTP_TYPE_UNSPECIFIED = Payload.OtpType.V(0) |
||||
|
OTP_TYPE_HOTP = Payload.OtpType.V(1) |
||||
|
OTP_TYPE_TOTP = Payload.OtpType.V(2) |
||||
|
|
||||
|
class OtpParameters(google.protobuf.message.Message): |
||||
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor = ... |
||||
|
SECRET_FIELD_NUMBER: builtins.int |
||||
|
NAME_FIELD_NUMBER: builtins.int |
||||
|
ISSUER_FIELD_NUMBER: builtins.int |
||||
|
ALGORITHM_FIELD_NUMBER: builtins.int |
||||
|
DIGITS_FIELD_NUMBER: builtins.int |
||||
|
TYPE_FIELD_NUMBER: builtins.int |
||||
|
COUNTER_FIELD_NUMBER: builtins.int |
||||
|
secret: builtins.bytes = ... |
||||
|
name: typing.Text = ... |
||||
|
issuer: typing.Text = ... |
||||
|
algorithm: global___Payload.Algorithm.V = ... |
||||
|
digits: global___Payload.DigitCount.V = ... |
||||
|
type: global___Payload.OtpType.V = ... |
||||
|
counter: builtins.int = ... |
||||
|
|
||||
|
def __init__(self, |
||||
|
*, |
||||
|
secret : builtins.bytes = ..., |
||||
|
name : typing.Text = ..., |
||||
|
issuer : typing.Text = ..., |
||||
|
algorithm : global___Payload.Algorithm.V = ..., |
||||
|
digits : global___Payload.DigitCount.V = ..., |
||||
|
type : global___Payload.OtpType.V = ..., |
||||
|
counter : builtins.int = ..., |
||||
|
) -> None: ... |
||||
|
def ClearField(self, field_name: typing_extensions.Literal[u"algorithm",b"algorithm",u"counter",b"counter",u"digits",b"digits",u"issuer",b"issuer",u"name",b"name",u"secret",b"secret",u"type",b"type"]) -> None: ... |
||||
|
|
||||
|
OTP_PARAMETERS_FIELD_NUMBER: builtins.int |
||||
|
VERSION_FIELD_NUMBER: builtins.int |
||||
|
BATCH_SIZE_FIELD_NUMBER: builtins.int |
||||
|
BATCH_INDEX_FIELD_NUMBER: builtins.int |
||||
|
BATCH_ID_FIELD_NUMBER: builtins.int |
||||
|
version: builtins.int = ... |
||||
|
batch_size: builtins.int = ... |
||||
|
batch_index: builtins.int = ... |
||||
|
batch_id: builtins.int = ... |
||||
|
|
||||
|
@property |
||||
|
def otp_parameters(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Payload.OtpParameters]: ... |
||||
|
|
||||
|
def __init__(self, |
||||
|
*, |
||||
|
otp_parameters : typing.Optional[typing.Iterable[global___Payload.OtpParameters]] = ..., |
||||
|
version : builtins.int = ..., |
||||
|
batch_size : builtins.int = ..., |
||||
|
batch_index : builtins.int = ..., |
||||
|
batch_id : builtins.int = ..., |
||||
|
) -> None: ... |
||||
|
def ClearField(self, field_name: typing_extensions.Literal[u"batch_id",b"batch_id",u"batch_index",b"batch_index",u"batch_size",b"batch_size",u"otp_parameters",b"otp_parameters",u"version",b"version"]) -> None: ... |
||||
|
global___Payload = Payload |
@ -0,0 +1,19 @@ |
|||||
|
import pytest |
||||
|
|
||||
|
from src.decoder import decode_secret |
||||
|
|
||||
|
|
||||
|
@pytest.mark.parametrize( |
||||
|
'secret,expected_result', |
||||
|
[ |
||||
|
(b'Hello!\xde\xad\xbe\xef', 'JBSWY3DPEHPK3PXP', ), |
||||
|
(b'Hello!', 'JBSWY3DPEE',), |
||||
|
(b'\xde\xad\xbe\xef', '32W353Y',), |
||||
|
], |
||||
|
) |
||||
|
def test_decode_secret(secret, expected_result): |
||||
|
# act |
||||
|
result = decode_secret(secret) |
||||
|
|
||||
|
# assert |
||||
|
assert result == expected_result |
@ -0,0 +1,31 @@ |
|||||
|
from src.decoder import decoded_data |
||||
|
|
||||
|
|
||||
|
def test_decoded_data__expected(): |
||||
|
# arrange |
||||
|
data = ['CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', ] |
||||
|
|
||||
|
# act |
||||
|
generator = decoded_data(data) |
||||
|
result = list(generator) |
||||
|
|
||||
|
# assert |
||||
|
assert result == [b'\n1\n\nHello!\xde\xad\xbe\xef\x12\x18Example:alice@google.com\x1a\x07Example0\x02', ] |
||||
|
|
||||
|
|
||||
|
def test_decoded_data__list__expected(): |
||||
|
# arrange |
||||
|
data = [ |
||||
|
'CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
'CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
] |
||||
|
|
||||
|
# act |
||||
|
generator = decoded_data(data) |
||||
|
result = list(generator) |
||||
|
|
||||
|
# assert |
||||
|
assert result == [ |
||||
|
b'\n1\n\nHello!\xde\xad\xbe\xef\x12\x18Example:alice@google.com\x1a\x07Example0\x02', |
||||
|
b'\n1\n\nHello!\xde\xad\xbe\xef\x12\x18Example:alice@google.com\x1a\x07Example0\x02', |
||||
|
] |
@ -0,0 +1,18 @@ |
|||||
|
from src.decoder import get_otpauth_url |
||||
|
from src.otpauth_migration_pb2 import Payload |
||||
|
|
||||
|
|
||||
|
def test_get_otpauth_url(): |
||||
|
# arrange |
||||
|
otp = Payload.OtpParameters( |
||||
|
secret=b'Hello!\336\255\276\357', |
||||
|
name='Example:alice@google.com', |
||||
|
issuer='Example', |
||||
|
type=2 |
||||
|
) |
||||
|
|
||||
|
# act |
||||
|
result = get_otpauth_url(otp) |
||||
|
|
||||
|
# assert |
||||
|
assert result == 'otpauth://totp/Example%3Aalice%40google.com?issuer=Example&secret=JBSWY3DPEHPK3PXP' |
@ -0,0 +1,18 @@ |
|||||
|
from src.decoder import get_url_params |
||||
|
from src.otpauth_migration_pb2 import Payload |
||||
|
|
||||
|
|
||||
|
def test_get_url_params(): |
||||
|
# arrange |
||||
|
otp = Payload.OtpParameters( |
||||
|
secret=b'Hello!\336\255\276\357', |
||||
|
name='Example:alice@google.com', |
||||
|
issuer='Example', |
||||
|
type=2 |
||||
|
) |
||||
|
|
||||
|
# act |
||||
|
result = get_url_params(otp) |
||||
|
|
||||
|
# assert |
||||
|
assert result == 'issuer=Example&secret=JBSWY3DPEHPK3PXP' |
@ -0,0 +1,31 @@ |
|||||
|
import pytest |
||||
|
from click import BadParameter |
||||
|
|
||||
|
from src.decoder import validate_migration |
||||
|
|
||||
|
|
||||
|
def test_validate_migration__migration__ok(): |
||||
|
# arrange |
||||
|
migration = 'otpauth-migration://offline?data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC' |
||||
|
|
||||
|
# act |
||||
|
result = validate_migration(None, None, migration) |
||||
|
|
||||
|
# assert |
||||
|
assert result == ['CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC'] |
||||
|
|
||||
|
|
||||
|
@pytest.mark.parametrize( |
||||
|
'broken_migration', |
||||
|
[ |
||||
|
'otpauth-migration://online?data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
'CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
'offline?data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
'otpauth-migration://online?data=Cu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
'data=CjEKCkhlbGxvId6tvu8SGEV4YW1wbGU6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZTAC', |
||||
|
] |
||||
|
) |
||||
|
def test_validate_migration__broken_migration__raise(broken_migration): |
||||
|
# act & assert |
||||
|
with pytest.raises(BadParameter): |
||||
|
validate_migration(None, None, broken_migration) |
@ -0,0 +1,12 @@ |
|||||
|
[tox] |
||||
|
envlist = py39 |
||||
|
|
||||
|
[testenv] |
||||
|
setenv = |
||||
|
COVERAGE_FILE = {envlogdir}/.coverage |
||||
|
PYTHONPATH = src |
||||
|
commands = |
||||
|
coverage run --source=src --module pytest --verbose tests |
||||
|
coverage report --show-missing |
||||
|
isort . |
||||
|
mypy src --pretty |
@ -0,0 +1,3 @@ |
|||||
|
pillow |
||||
|
pyzbar |
||||
|
qrcode |
Loading…
Reference in new issue