Compare commits

..

159 Commits

Author SHA1 Message Date
scito 66c68fd9ef fix Python 3.7 compatibility 2023-01-02 22:41:30 +01:00
scito 6cfbc10e69 build wheel 2023-01-02 22:18:54 +01:00
scito 064fe81b2e docker remove python3-opencv, but add required libs; add debug mode 2023-01-02 22:11:56 +01:00
scito cd5160f123 fix Alpine tests 2023-01-02 20:29:37 +01:00
scito e1c8568ba2 fix protoc generation 2023-01-02 19:28:11 +01:00
scito ef0fbc3586 update README 2023-01-02 19:09:28 +01:00
scito b86c4f9a61 add ignore duplicate entries option and add quiet test; fixed -k stdout 2023-01-02 17:30:46 +01:00
scito 722009b172 build: add generate result files option 2023-01-02 14:16:30 +01:00
scito 1c106150b0 sort docker imports 2023-01-02 13:53:29 +01:00
scito 77e23b4ae4 refactor cv2 window and logging
- increase font and cut if too long
- refactor logging
- extract key handling
- refactor big methods
2023-01-02 13:52:04 +01:00
scito fe3e371897 build: print restult at the end 2023-01-02 12:50:41 +01:00
scito 7c6d341270 rename github urls; update descriptions 2023-01-01 23:21:29 +01:00
scito 160a558825 some renaming and README updating 2023-01-01 22:47:12 +01:00
scito 8d8b993f12 add CV2 screenshot 2023-01-01 20:15:38 +01:00
scito e177f860e1 abort instead of assertion 2023-01-01 19:55:28 +01:00
scito 8545dab7a5 extract draw_box and print_text functions 2023-01-01 19:22:46 +01:00
scito 16047a5b15 fix test compatibility with Alpine Linux 2023-01-01 19:19:41 +01:00
scito 604c461549 rename to build.sh 2023-01-01 19:19:21 +01:00
scito f5acd1dee9 test docker locally after build, easier to reproduce 2023-01-01 17:56:06 +01:00
scito 1086e28056 fix pylint and mypy problems 2023-01-01 17:45:50 +01:00
scito 2c0cfd83ee docker: pip install -U 2023-01-01 17:38:08 +01:00
scito a3bda6ff8e make PYTHON workaround uniform 2023-01-01 17:25:24 +01:00
scito 67c4f737c4 do catch AssertionError in camera capture 2023-01-01 15:21:18 +01:00
scito fff74fc638 handle errors while reading QR from camera; log errors 2023-01-01 15:12:24 +01:00
scito 19c8e9df23 update docs 2023-01-01 10:03:44 +01:00
scito 13fcdcd022 fix problem of outdated colorama 2023-01-01 01:22:06 +01:00
scito 91b9b3671c ci: add missing colorama 2023-01-01 01:02:39 +01:00
scito be6b9c8a7c ci: mypy only for latest Python on ubuntu 2023-01-01 00:57:18 +01:00
scito 3d61f1d316 ci: use pip install -e . for smoke tests 2023-01-01 00:56:19 +01:00
scito a8559db6e0 ci: mypy only for latest Python on ubuntu 2023-01-01 00:52:28 +01:00
scito 9f725b227f ci: use requirements.txt for smoke tests 2023-01-01 00:48:52 +01:00
scito 869c404489 fix fileinput.input encoding only since Python 3.10 2023-01-01 00:41:11 +01:00
scito 003e122808 ignore types for stdout.reconfigure 2023-01-01 00:22:09 +01:00
scito b3fc854078 colored warn and error messages
- add log_warn() and log_error()
- adapt tests
2023-01-01 00:14:56 +01:00
scito fc1619d9c7 disable verbose unittest for Windows 2022-12-31 21:34:28 +01:00
scito 5be6e9c322 fix finput encoding problem on Windows 2022-12-31 21:27:40 +01:00
scito 739ae4c012 fix pytest.skipif 2022-12-31 21:16:19 +01:00
scito 1af6fe3161 fix camera type and enhance readme with pyzbar problem 2022-12-31 21:02:05 +01:00
scito e311386a15 skip verbose tests for windows as there are encoding problems 2022-12-31 20:42:40 +01:00
scito 496564a605 avoid AttributeError in tests
StringIO in tests do not have all attributes, ignore it
2022-12-31 20:19:21 +01:00
scito 6406fcaef7 set encoding to utf-8 for stdout, needed for Windows 2022-12-31 19:52:09 +01:00
scito 7bb92f00b2 avoid mypy problems for duplicate Final
src/extract_otp_secrets.py:63: error: Incompatible import of "Final"
(imported name has type "typing_extensions._SpecialForm", local name has type "typing._SpecialForm")
[assignment]
2022-12-31 19:42:53 +01:00
scito 965f721caf ci_docker: run smoke tests before building 2022-12-31 19:20:01 +01:00
scito 61cca0c476 import Final from __future__ for Python 3.7 2022-12-31 19:11:37 +01:00
scito ebd4d61f5f try workaround for Python 3.7: must use () for assignments 2022-12-31 19:06:57 +01:00
scito e058010be3 fix docker invalid tags from comments 2022-12-31 18:59:01 +01:00
scito 463a9851be try workaround for Python 3.7: avoid Final in tuple assignments 2022-12-31 18:55:16 +01:00
scito dcbb128e7c try workaround for Python 3.7: do not use Types in Final 2022-12-31 18:51:26 +01:00
scito 1b572fc9ab fix pipenv problem 2022-12-31 18:40:26 +01:00
scito c3e9883216 try workaround for Python 3.7 NORMAL_COLOR = 255, 0, 255 problem 2022-12-31 18:21:13 +01:00
scito 3f9f7d2b8a better formatting of help 2022-12-31 18:00:49 +01:00
scito 0212e54f42 ci: runs on windows, exclude linting for Python 3.7 2022-12-31 17:52:19 +01:00
scito 3558eba93b use detect_and_decode since qrreader is fixed 2022-12-31 17:30:24 +01:00
scito 5225af0621 update README with --qr option 2022-12-31 16:42:12 +01:00
scito 1f04dd71e2 allow to choose qr reader for images 2022-12-31 15:54:21 +01:00
scito 2dea161cdc add argument for initial setting of qr_mode 2022-12-31 12:44:16 +01:00
scito f731530f57 improve handling of wrong urls
- adapt tests
- improve messages for files
- show red box camera
2022-12-31 11:32:07 +01:00
scito 4c0bb8dc61 vscode debug: fix path to script: add missing src/ 2022-12-31 11:26:11 +01:00
scito ad9c4a22db ci_docker: upload to only one repo 2022-12-31 11:25:33 +01:00
scito 2cdf2480a0 make bulding docker optional in script 2022-12-31 11:25:02 +01:00
scito 5aa1a35b8f add licence label to docker images 2022-12-31 11:23:44 +01:00
scito 3f3903cc81 add version to PYTHON todos 2022-12-30 21:22:03 +01:00
scito 97e4f084cb mv docker extract_otp_secrets_no_qr_reader -> extract_otp_secrets_only_txt 2022-12-30 20:43:51 +01:00
scito 549c128fb7 renaming extract_otp_secret_keys -> extract_otp_secrets and test file names 2022-12-30 20:37:38 +01:00
scito 10ff533a42 downgrade opencv for macos in requirements.txt and Pipfile 2022-12-30 19:26:45 +01:00
scito 7eb6f036ab ci: tryout downgrade opencv for macos 2022-12-30 18:51:13 +01:00
scito 652ecf57f0 ci: workaround macOS pytest segfauls -> exclude macOS (2) 2022-12-30 18:33:26 +01:00
scito 9592e6ebfe ci: workaround macOS pytest segfauls -> exclude macOS 2022-12-30 18:29:23 +01:00
scito d6c285e59d ci: segfault: try python -m pytest 2022-12-30 18:23:31 +01:00
scito 5eed47364e ci: revert try out changes 2022-12-30 18:18:15 +01:00
scito 26e4632f90 ci: another try, install module 2022-12-30 18:14:16 +01:00
scito c84ca46861 ci: try to avoid random test failures 2022-12-30 18:09:25 +01:00
scito 63f5ab37c4 docu pyproject.toml 2022-12-30 18:02:22 +01:00
scito f97d7143c5 ci: fix mypy and pytest-cov after clean 2022-12-30 17:49:46 +01:00
scito 0566683203 reenable pytest --import-mode=importlib 2022-12-30 17:31:34 +01:00
scito ee404576d5 set package_dir=src and clean option 2022-12-30 17:14:49 +01:00
scito 60d7362eee add protobuf_generated_python to py-modules 2022-12-30 15:47:02 +01:00
scito 1beba7587f enable setuptools-git-versioning 2022-12-30 15:44:58 +01:00
scito 144c9e6320 fixes after change to src-layout 2022-12-30 15:31:41 +01:00
scito 3e4476e317 change to src-layout 2022-12-30 12:37:05 +01:00
scito 7f5d4b37ee initial pyproject.toml 2022-12-30 11:14:15 +01:00
scito 82e43172c3 update setup.py 2022-12-30 09:23:31 +01:00
scito 149a548610 upgrade opencv 4.7.0 & run program at the end 2022-12-30 08:14:13 +01:00
scito d8de89de36 improve README, add docker ci badge 2022-12-30 02:30:16 +01:00
scito 3c164fea28 coverage from yellowgreen to brightgreen 2022-12-30 02:01:10 +01:00
scito 23d8cfa151 ci: Pytest coverage comment only for 3.x and ubuntu-latest 2022-12-30 01:58:54 +01:00
scito f5ee59368e python 3.7 compatibility: use TypedDict from typing_extensions 2022-12-30 01:44:11 +01:00
scito b2a877061c add typing_extensions for compatibility 2022-12-30 01:37:13 +01:00
scito c525c06480 quit on window close click 2022-12-30 01:22:12 +01:00
scito fb43c6793c type hinting fixes 2022-12-30 01:22:05 +01:00
scito 58fc1b85ac type compatibility for Python < 3.11 2022-12-30 01:07:39 +01:00
scito 04d864c093 add code coverage bade & ci: fix pytest-cov 2022-12-30 00:58:52 +01:00
scito 51094a1a18 use PathLike type instead of str | Path 2022-12-29 23:17:31 +01:00
scito a5768ba1e6 Workaround for PYTHON < 3.10: use Union[int, None] instead of int | None 2022-12-29 22:34:07 +01:00
scito faafb61241 fix type hint compatibility with Python < 3.11 2022-12-29 22:12:07 +01:00
scito d5a088135e ci: if matrix.python-version 2022-12-29 22:04:52 +01:00
scito 45a9693586 ci: fix if ${{ matrix.python-version }} 2022-12-29 21:58:44 +01:00
scito 66b41d86e6 ci: use ${{ }} for mypy python check 2022-12-29 21:52:54 +01:00
scito 89564448c6 ci: fix mypy if condition 2022-12-29 21:41:11 +01:00
scito 9ab33bd02b sort imports 2022-12-29 21:34:14 +01:00
scito f4ab540283 rename protobuf module as pb 2022-12-29 21:32:19 +01:00
scito 201e6510f8 add type hints (Python 3.11) 2022-12-29 21:29:20 +01:00
scito f933cd0d32 initial mypy type checking 2022-12-29 16:30:18 +01:00
scito f4389ca8a3 fix linting 2022-12-29 15:52:17 +01:00
scito b89a338246 enable mypy type checking 2022-12-29 14:24:12 +01:00
scito 631bacc409 use tmp_path fixture instead of clean_up() in pytests 2022-12-29 13:44:19 +01:00
scito 833afa7c13 use 3.11-slim-bullseye, add missing package 2022-12-29 11:50:19 +01:00
scito 4209a5db3d move abort to end, use SystemExit instead; improve colors 2022-12-29 11:18:57 +01:00
scito d9a4c7ca9f fix docker image digest 2022-12-29 04:36:31 +01:00
scito 829fe65b1e ci: pip install pytest-mock 2022-12-29 04:24:00 +01:00
scito c90526dcf2 fix undefined name 'abort' and 'qreader' 2022-12-29 04:15:36 +01:00
scito 47e84e4462 run docker capture version 2022-12-29 03:19:09 +01:00
scito b4931856ba apk add --no-cache nano zlib jpeg && re-enable tests 2022-12-29 02:34:29 +01:00
scito f532dc668d indent_size = 2 for yml 2022-12-29 01:59:54 +01:00
scito 1dee86668a rename ARG run_tests to RUN_TESTS 2022-12-29 01:48:00 +01:00
scito aa0de699fe disable tests for alpine 2022-12-29 01:43:22 +01:00
scito 7e684ff19e --build-arg run_tests=false 2022-12-29 01:35:43 +01:00
scito b159b9e70d apk add musl-dev and apk del .build-deps 2022-12-29 01:13:48 +01:00
scito 951878d027 apk add gcc 2022-12-29 01:02:41 +01:00
scito 2a44bbfa27 use $(apk --print-arch) 2022-12-29 00:53:47 +01:00
scito 540ae7438d use $(uname -m) 2022-12-29 00:42:13 +01:00
scito c346c085b6 apk add python3-dev py3-setuptools 2022-12-29 00:33:52 +01:00
scito 7cb3b2ac21 always apk add on alpine 2022-12-29 00:29:26 +01:00
scito 0eb5014eb0 ci_docker: add docker/setup-buildx-action@v2 2022-12-29 00:25:29 +01:00
scito d4f5eb243e change to "$TARGETARCH" == "arm64" 2022-12-29 00:02:14 +01:00
scito b05decc10f apk add zlib-dev jpeg-dev only for alpine linux/arm64 2022-12-28 23:58:09 +01:00
scito 21ebccbba5 apk add zlib-dev 2022-12-28 23:44:30 +01:00
scito 912825034f add zlib for alpine/arm64 docker image 2022-12-28 23:37:54 +01:00
scito 95e7d73173 mock no_args tests 2022-12-28 23:37:21 +01:00
scito 9f0872c2d0 extract from camera
- add help description
- use f-strings
- handle plural correctly
- rename methods, use otp_url instead of line
- remove importlib.util
- move cv2 imports to top
- remove unnecessary global delcarations
- group image tests
2022-12-28 22:43:40 +01:00
scito 7964c687f6 make running tests optional in docker build 2022-12-28 09:02:02 +01:00
scito 1d0b568b1e setup QEMU 2022-12-27 11:36:53 +01:00
scito aaa7bd3da1 build also on arm64 2022-12-27 10:34:32 +01:00
scito 5ab5f84ff3 add docker label for source 2022-12-27 01:11:36 +01:00
scito a4c4badd54 fix docker hub username 2022-12-27 00:57:49 +01:00
scito f272c35a1f enable docker push in ci_docker 2022-12-27 00:52:14 +01:00
scito e4e5271c0f github actions docker build no_qr_reader 2022-12-27 00:43:30 +01:00
scito 158564e79a fix to_bytes() mandatory byte_order 2022-12-27 00:18:37 +01:00
scito 672d18a5ca build docker images, run tests in docker build
- qreader_available flag
- echo commands in upgrade_deps.sh
2022-12-26 23:57:38 +01:00
scito 0490e227e1 docker image with qreader, 2nd image without qreader
- organize imports
- add qreader pytest.mark
- relaxed mode for pytest
- run tests in docker
- more tests
2022-12-26 18:31:09 +01:00
scito 2bcaa35251 organize imports 2022-12-25 11:00:15 +01:00
scito b0b4c29e7b improve README 2022-12-24 15:50:44 +01:00
scito e754befb52 refactor; update setup.py
more verbose logging
better error messages
2022-12-24 15:30:17 +01:00
scito 06b8efff62 add zypper and dnf for libzbar0 2022-12-24 05:31:17 +01:00
scito 5d0feacdba update README 2022-12-24 05:12:52 +01:00
scito 343520acb8 support multiple infiles 2022-12-24 04:48:12 +01:00
scito c2d7c905ff handle wrong stdin streams 2022-12-24 04:19:43 +01:00
scito bc329e24d5 renable libzbar 2022-12-24 03:29:14 +01:00
scito 4612ab6e7f catch zbar import error 2022-12-24 03:25:10 +01:00
scito 05db190de3 test qreader import 2022-12-24 03:12:06 +01:00
scito 0ad3c2d8ed change name to nightly tests 2022-12-24 03:06:03 +01:00
scito 31bb2909da fix ci jobs 2022-12-24 03:05:07 +01:00
scito c1a55fb874 test pypy only in nightly builds 2022-12-24 03:02:08 +01:00
scito 82da427d1a refactor code: extract method 2022-12-24 02:56:40 +01:00
scito af0d7ffd5d dynamic import of QReader
since this module has a dependency to zbar lib
2022-12-24 02:46:36 +01:00
scito 9a308b148f fix macOS ci 2022-12-24 02:37:16 +01:00
scito cd07851e30 install zbar lib 2022-12-24 02:29:43 +01:00
scito f4934192ae WIP 2022-12-24 01:59:35 +01:00
qwertyca 483fcc0163 Add the ability so provide an image file as the infile. If the file contains a QR code generated by Google Authenticator's "Transfer Accounts" function, it will be decoded directly in a single step. This is meant to help users who need to access their secrets from Google Authenticator but don't have a QR code decoder and don't want to use an online one due to security concerns. 2022-12-23 10:17:04 +01:00
13 changed files with 133 additions and 311 deletions
+1 -6
View File
@@ -2,8 +2,6 @@ name: tests
# https://docs.github.com/de/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/using-workflows
# https://docs.github.com/en/actions/learn-github-actions/contexts
# https://docs.github.com/en/actions/learn-github-actions/expressions
on:
push:
@@ -67,7 +65,4 @@ jobs:
with:
pytest-coverage-path: ./pytest-coverage.txt
junitxml-path: ./pytest.xml
if: |
matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
&& !contains(github.ref, 'refs/tags/')
if: matrix.python-version == '3.x' && matrix.platform == 'ubuntu-latest'
+1 -2
View File
@@ -2,8 +2,6 @@ name: docker
# https://docs.github.com/de/actions/using-workflows/workflow-syntax-for-github-actions
# https://docs.github.com/en/actions/using-workflows
# https://docs.github.com/en/actions/learn-github-actions/contexts
# https://docs.github.com/en/actions/learn-github-actions/expressions
# How to setup: https://event-driven.io/en/how_to_buid_and_push_docker_image_with_github_actions/
# How to run: https://aschmelyun.com/blog/using-docker-run-inside-of-github-actions/
@@ -11,6 +9,7 @@ name: docker
on:
# run it on push to the default repository branch
push:
branches: [master]
schedule:
# Run weekly on default branch
- cron: '47 3 * * 6'
+10 -12
View File
@@ -4,26 +4,24 @@ verify_ssl = true
name = "pypi"
[packages]
colorama = ">=0.4.6"
opencv-contrib-python = "*"
# for macOS: opencv-contrib-python = "<=4.7.0"
# for PYTHON <= 3.7: typing_extensions = "*"
pillow = "*"
protobuf = "*"
qrcode = "*"
pillow = "*"
qreader = "*"
opencv-contrib-python = "*"
colorama = ">=0.4.6"
# for macOS: opencv-contrib-python = "<=4.7.0"
# for PYTHON <= 3.7: typing_extensions = "*"
[dev-packages]
build = "*"
flake8 = "*"
mypy = "*"
mypy-protobuf = "*"
pylint = "*"
pytest = "*"
pytest-cov = "*"
pytest-mock = "*"
types-protobuf = "*"
pytest-cov = "*"
wheel = "*"
flake8 = "*"
pylint = "*"
mypy = "*"
types-protobuf = "*"
[requires]
python_version = "3.11"
Generated
+1 -45
View File
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "13e2cc849fbc56593a2179a51a091717bcd4baeb9235b0843bb3abd8a9d1c698"
"sha256": "25b244c44cb891ac15ef20c4011eb043b87fb1f112396d68f470d0bb362e97f7"
},
"pipfile-spec": 6,
"requires": {
@@ -220,14 +220,6 @@
"markers": "python_version >= '3.6'",
"version": "==22.2.0"
},
"build": {
"hashes": [
"sha256:1a07724e891cbd898923145eb7752ee7653674c511378eb9c7691aab1612bc3c",
"sha256:38a7a2b7a0bdc61a42a0a67509d88c71ecfc37b393baba770fae34e20929ff69"
],
"index": "pypi",
"version": "==0.9.0"
},
"coverage": {
"extras": [
"toml"
@@ -395,14 +387,6 @@
],
"version": "==0.4.3"
},
"mypy-protobuf": {
"hashes": [
"sha256:7d75a079651b105076776a35a5405e3fa773b8a167118f1b712e443e9a6c18a2",
"sha256:da33dfde7547ff57e5ba5564126cbfa114f14413b2fa50759b1fa5de1e4ab511"
],
"index": "pypi",
"version": "==3.4.0"
},
"packaging": {
"hashes": [
"sha256:2198ec20bd4c017b8f9717e00f0c8714076fc2fd93816750ab48e2c41de2cfd3",
@@ -411,14 +395,6 @@
"markers": "python_version >= '3.7'",
"version": "==22.0"
},
"pep517": {
"hashes": [
"sha256:4ba4446d80aed5b5eac6509ade100bff3e7943a8489de249654a5ae9b33ee35b",
"sha256:ae69927c5c172be1add9203726d4b84cf3ebad1edcd5f71fcdc746e66e829f59"
],
"markers": "python_version >= '3.6'",
"version": "==0.13.0"
},
"platformdirs": {
"hashes": [
"sha256:83c8f6d04389165de7c9b6f0c682439697887bca0aa2f1c87ef1826be3584490",
@@ -435,26 +411,6 @@
"markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"protobuf": {
"hashes": [
"sha256:1f22ac0ca65bb70a876060d96d914dae09ac98d114294f77584b0d2644fa9c30",
"sha256:237216c3326d46808a9f7c26fd1bd4b20015fb6867dc5d263a493ef9a539293b",
"sha256:27f4d15021da6d2b706ddc3860fac0a5ddaba34ab679dc182b60a8bb4e1121cc",
"sha256:299ea899484ee6f44604deb71f424234f654606b983cb496ea2a53e3c63ab791",
"sha256:3d164928ff0727d97022957c2b849250ca0e64777ee31efd7d6de2e07c494717",
"sha256:6ab80df09e3208f742c98443b6166bcb70d65f52cfeb67357d52032ea1ae9bec",
"sha256:78a28c9fa223998472886c77042e9b9afb6fe4242bd2a2a5aced88e3f4422aa7",
"sha256:7cd532c4566d0e6feafecc1059d04c7915aec8e182d1cf7adee8b24ef1e2e6ab",
"sha256:89f9149e4a0169cddfc44c74f230d7743002e3aa0b9472d8c28f0388102fc4c2",
"sha256:a53fd3f03e578553623272dc46ac2f189de23862e68565e83dde203d41b76fc5",
"sha256:b135410244ebe777db80298297a97fbb4c862c881b4403b71bac9d4107d61fd1",
"sha256:b98d0148f84e3a3c569e19f52103ca1feacdac0d2df8d6533cf983d1fda28462",
"sha256:d1736130bce8cf131ac7957fa26880ca19227d4ad68b4888b3be0dea1f95df97",
"sha256:f45460f9ee70a0ec1b6694c6e4e348ad2019275680bd68a1d9314b8c7e01e574"
],
"index": "pypi",
"version": "==4.21.12"
},
"pycodestyle": {
"hashes": [
"sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053",
+26 -42
View File
@@ -1,7 +1,7 @@
# Extract TOTP/HOTP secrets from QR codes exported by two-factor authentication apps
# Extract secrets from QR codes exported by two-factor authentication apps
[![CI tests](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml/badge.svg)](https://github.com/scito/extract_otp_secrets/actions/workflows/ci.yml)
![coverage](https://img.shields.io/badge/coverage-93%25-brightgreen)
![coverage](https://img.shields.io/badge/coverage-96%25-brightgreen)
[![CI docker](https://github.com/scito/extract_otp_secrets/actions/workflows/ci_docker.yml/badge.svg)](https://github.com/scito/extract_otp_secrets/actions/workflows/ci_docker.yml)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/protobuf)
[![GitHub Pipenv locked Python version](https://img.shields.io/github/pipenv/locked/python-version/scito/extract_otp_secrets)](https://github.com/scito/extract_otp_secrets/blob/master/Pipfile.lock)
@@ -13,15 +13,15 @@
---
The Python script `extract_otp_secrets.py` extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as "Google Authenticator".
The exported QR codes from authentication apps can be read in three ways:
The export QR codes from authentication apps can be provided in three ways to this script:
1. Capture from the system camera using a GUI, 🆕
2. Read image files containing the QR codes, and 🆕
3. Read text files containing the QR code data generated by third-party QR readers.
1. Capture from the system camera in a GUI,
2. Image files containing the QR codes, and
3. Text files containing the QR code data generated by QR readers.
The secret and otp values can be exported to json or csv files, as well as printed or saved to PNG images.
**The project and the script were renamed from `extract_otp_secret_keys` to `extract_otp_secrets` in version 2.0.**
This script/project was renamed from extract_otp_secret_keys to extract_otp_secrets in version 2.0.0.
## Installation
@@ -70,7 +70,7 @@ OpenCV requires [Visual C++ redistributable 2015](https://www.microsoft.com/en-u
## Usage
### Capture QR codes from camera (🆕 since version 2.0)
### Capture QR codes from camera (since version 2.0.0)
1. Open "Google Authenticator" app on the mobile phone
2. Export the QR codes from "Google Authenticator" app
@@ -81,13 +81,7 @@ OpenCV requires [Visual C++ redistributable 2015](https://www.microsoft.com/en-u
![CV2 Capture from camera screenshot](cv2_capture_screenshot.png)
Detected QR codes are surrounded with a frame. The color of the frame indicates the extracting result:
* Green: The QR code is detected, decoded and the OTP secret was successfully extracted.
* Red: The QR code is detected and decoded, but could not be successfully extracted. This is the case if a QR code not containing OTP data is captured.
* Magenta: The QR code is detected, but could not be decoded. The QR code should be presented better to the camera or another QR reader could be used.
### With builtin QR decoder from image files (🆕 since version 2.0)
### With builtin QR decoder from image files (since version 2.0.0)
1. Open "Google Authenticator" app on the mobile phone
2. Export the QR codes from "Google Authenticator" app
@@ -183,13 +177,13 @@ python extract_otp_secrets.py = < example_export.png</pre>
* Free and open source
* Supports Google Authenticator exports (and compatible apps like Aegis Authenticator)
* Captures the the QR codes directly from the camera using different QR code libraries (based on OpenCV) (🆕 since v2.0)
* Captures the the QR codes directly from the camera using different QR code libraries (based on OpenCV)
* ZBAR: [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar) - fast and reliable, good for images and video capture (default and recommended)
* QREADER: [QReader](https://github.com/Eric-Canas/QReader)
* QREADER_DEEP: [QReader](https://github.com/Eric-Canas/QReader) - very slow in GUI
* CV2: [QRCodeDetector](https://docs.opencv.org/4.x/de/dc3/classcv_1_1QRCodeDetector.html)
* CV2_WECHAT: [WeChatQRCode](https://docs.opencv.org/4.x/dd/d63/group__wechat__qrcode.html)
* Supports [TOTP](https://www.ietf.org/rfc/rfc6238.txt) and [HOTP](https://www.ietf.org/rfc/rfc4226.txt) standards
* Supports TOTP and HOTP standards
* Generates QR codes
* Exports to various formats:
* CSV
@@ -197,8 +191,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
* Dedicated CSV for KeePass
* QR code images
* Supports reading from stdin and writing to stdout, thus pipes can be used
* Handles multiple input files (🆕 since v2.0)
* Reads QR codes images: (See [OpenCV docu](https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56)) (🆕 since v2.0)
* Reads QR codes images: (See [OpenCV docu](https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56))
* Portable Network Graphics - *.png
* WebP - *.webp
* JPEG files - *.jpeg, *.jpg, *.jpe
@@ -206,8 +199,8 @@ python extract_otp_secrets.py = < example_export.png</pre>
* Windows bitmaps - *.bmp, *.dib
* JPEG 2000 files - *.jp2
* Portable image format - *.pbm, *.pgm, *.ppm *.pxm, *.pnm
* Prints errors and warnings to stderr (🆕 since v2.0)
* Prints colored output (🆕 since v2.0)
* Prints errors and warnings to stderr
* Prints colored output
* Many ways to run the script:
* Native Python
* pipenv
@@ -216,7 +209,7 @@ python extract_otp_secrets.py = < example_export.png</pre>
* Docker
* VSCode devcontainer
* devbox
* Prebuilt Docker images provided for amd64 and arm64 (🆕 since v2.0)
* Prebuilt Docker images provided for amd64 and arm64
* Compatible with major platforms:
* Linux
* macOS
@@ -315,7 +308,7 @@ curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/examp
```
git clone https://github.com/scito/extract_otp_secrets.git
pip install -U -e extract_otp_secrets
python -m extract_otp_secrets extract_otp_secrets/example_export.txt
python -m extract_otp_secrets example_export.txt
```
### pipenv
@@ -375,7 +368,7 @@ docker login -u USERNAME
curl -s https://raw.githubusercontent.com/scito/extract_otp_secrets/master/example_export.png | docker run --pull always -i --rm -v "$(pwd)":/files:ro scit0/extract_otp_secrets =
```
Capturing from camera in GUI window (X Window system required on host):
Capturing from camera in GUI (X Window system required on host):
```
docker run --pull always --rm -v "$(pwd)":/files:ro -i --device="/dev/video0:/dev/video0" --env="DISPLAY" -v /tmp/.X11-unix:/tmp/.X11-unix:ro scit0/extract_otp_secrets
@@ -454,7 +447,6 @@ Setup for running the tests in VSCode.
### Build
```
cd extract_otp_secrets/
pip install -U -e .
python src/extract_otp_secrets.py
@@ -471,23 +463,12 @@ pip install -U -r requirements.txt
### Build docker images
#### Debian (full functionality)
Build and run the app within the container:
```bash
docker build . -t extract_otp_secrets --pull --build-arg RUN_TESTS=false
```
Run tests in docker container:
```bash
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
```
#### Alpine (only text file processing)
```bash
docker build . -t extract_otp_secrets_only_txt --pull -f Dockerfile_only_txt --build-arg RUN_TESTS=false
```
@@ -495,13 +476,16 @@ docker build . -t extract_otp_secrets_only_txt --pull -f Dockerfile_only_txt --b
Run tests in docker container:
```bash
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_only_txt tests/extract_otp_secrets_test.py -k "not qreader" --relaxed
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
```
```bash
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets_only_txt extract_otp_secrets_test.py -k "not qreader" --relaxed
```
## Issues
* Segmentation fault on macOS with CV2 4.7.0: https://github.com/opencv/opencv/issues/23072
* CV2 window does not show icons: https://github.com/opencv/opencv-python/issues/585
* Known issue for macOS: https://github.com/opencv/opencv/issues/23072
## Problems and Troubleshooting
@@ -534,10 +518,10 @@ FileNotFoundError: Could not find module 'libiconv.dll' (or one of its dependenc
* [ZBar](https://github.com/mchehab/zbar) is an open source software suite for reading bar codes from various sources, including webcams.
* [Aegis Authenticator](https://github.com/beemdevelopment/Aegis) is a free, secure and open source 2FA app for Android.
* [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar) is a good QR code reader Python module
* [OpenCV](https://docs.opencv.org/4.x/) (CV2) Open Source Computer Vision library with [opencv-python](https://github.com/opencv/opencv-python)
* [Python QReader](https://github.com/Eric-Canas/QReader) Python QR code readers
* [Android OTP Extractor](https://github.com/puddly/android-otp-extractor) can extract your tokens from popular Android OTP apps and export them in a standard format or just display them as QR codes for easy importing. [Requires a _rooted_ Android phone.]
* [Python QReader](https://github.com/Eric-Canas/QReader)
* [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar)
* [OpenCV](https://docs.opencv.org/4.x/) (CV2) Open Source Computer Vision library with [opencv-python](https://github.com/opencv/opencv-python)
***
+40 -45
View File
@@ -138,8 +138,6 @@ PIPENV="$PYTHON -m pipenv"
FLAKE8="$PYTHON -m flake8"
MYPY="$PYTHON -m mypy"
# sudo ln -s /usr/bin/python3.11 /usr/bin/python
# Upgrade protoc
DEST="protoc"
@@ -149,26 +147,6 @@ echo -e "\nProtoc remote version $VERSION\n"
echo -e "Protoc local version: $OLDVERSION\n"
if $clean; then
cmd="docker image prune -f || echo 'No docker image pruned'"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="$PIP uninstall -y extract-otp-secrets || echo nothing done"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="$PIP freeze | grep -v -E '^-e|^#' | xargs sudo $PIP uninstall -y || echo nothing done"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="$PIP freeze --user | grep -v -E '^-e|^#' | xargs $PIP uninstall -y || echo nothing done"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="$PIP freeze | cut -d \"@\" -f1 | xargs pip uninstall -y || echo Nothing to do"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="rm -r dist/ build/ *.whl pytest.xml pytest-coverage.txt .coverage tests/reports || true; find . -name '*.pyc' -type f -delete; find . -name '__pycache__' -type d -exec rm -r {} \; || true; find . -name '*.egg-info' -type d -exec rm -r {} \; || true; find . -name '*_cache' -type d -exec rm -r {} \; || true; mkdir -p tests/reports;"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
@@ -182,10 +160,6 @@ if $clean; then
eval "$cmd"
fi
cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
if [ "$OLDVERSION" != "$VERSION" ] || ! $ignore_version_check; then
echo "Upgrade protoc from $OLDVERSION to $VERSION"
@@ -242,7 +216,7 @@ fi
# Upgrade pip requirements
cmd="pip install -U pip"
cmd="sudo pip install -U pip"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
@@ -252,6 +226,10 @@ cmd="$PIP install --use-pep517 -U -r requirements.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# Lint
LINT_OUT_FILE="tests/reports/flake8_results.txt"
@@ -276,21 +254,7 @@ cmd="$MYPY --strict src/*.py tests/*.py | tee $TYPE_CHECK_OUT_FILE"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# pip -e install
cmd="$PIP install -U -e ."
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="extract_otp_secrets example_export.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="extract_otp_secrets - < example_export.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# Test (needs module)
# Test
cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
@@ -323,9 +287,37 @@ cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# sudo pip
cmd="sudo $PIP install --use-pep517 -U -r requirements.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="sudo $PIP install --use-pep517 -U -r requirements-dev.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="sudo $PIP install -U pipenv"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# pip -e install (must be after other pip installs in order to have this environment for development)
cmd="$PIP install -U -e ."
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="extract_otp_secrets example_export.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="extract_otp_secrets - < example_export.txt"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
# Build wheel
cmd="$PIP wheel ."
cmd="$PIP wheel .
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
@@ -386,6 +378,10 @@ if $build_docker; then
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
cmd="docker image prune -f || echo 'No docker image pruned'"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
if $run_gui; then
cmd="docker run --rm -v "$(pwd)":/files:ro --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets &"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
@@ -406,7 +402,6 @@ cmd="cat $TYPE_CHECK_OUT_FILE $LINT_OUT_FILE $COVERAGE_OUT_FILE"
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
eval "$cmd"
line=$(printf '#%.0s' $(eval echo {1..$(( ($COLUMNS - 10) / 2))}))
echo -e "\n${greenBold}$line SUCCESS $line${reset}"
echo -e "\n${greenBold}SUCCESS${reset}"
quit
+5 -5
View File
@@ -29,15 +29,15 @@ classifiers = [
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
]
dependencies = [
"colorama>=0.4.6",
"opencv-contrib-python; sys_platform != 'darwin'",
"opencv-contrib-python<=4.7.0; sys_platform == 'darwin'",
"Pillow",
"protobuf",
"pyzbar",
"qrcode",
"Pillow",
"qreader",
"pyzbar",
"opencv-contrib-python<=4.7.0; sys_platform == 'darwin'",
"opencv-contrib-python; sys_platform != 'darwin'",
"typing_extensions; python_version<='3.7'",
"colorama>=0.4.6",
]
description = "Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps such as 'Google Authenticator'"
dynamic = ["version"]
+3 -4
View File
@@ -1,11 +1,10 @@
build
flake8
mypy
mypy-protobuf
types-protobuf
pylint
pytest
pytest-cov
pytest-mock
pytest-cov
setuptools
types-protobuf
wheel
build
+5 -5
View File
@@ -1,9 +1,9 @@
colorama>=0.4.6
opencv-contrib-python; sys_platform != 'darwin'
opencv-contrib-python<=4.7.0; sys_platform == 'darwin'
Pillow
protobuf
pyzbar
qrcode
Pillow
qreader
opencv-contrib-python<=4.7.0; sys_platform == 'darwin'
opencv-contrib-python; sys_platform != 'darwin'
pyzbar
typing_extensions; python_version<='3.7'
colorama>=0.4.6
+36 -36
View File
@@ -220,6 +220,42 @@ def extract_otps(args: Args) -> Otps:
return extract_otps_from_files(args)
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
if new_otps_count:
return SUCCESS_COLOR
else:
if otp_url:
return FAILURE_COLOR
else:
return NORMAL_COLOR
# TODO use cv2 types if available
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
pts = np.array([raw_pts], np.int32)
pts = pts.reshape((-1, 1, 2))
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
return pts
# TODO use cv2 types if available
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
out_text = text
if opposite_len:
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
if actual_width >= window_dim[WINDOW_WIDTH]:
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
if position == TextPosition.LEFT:
pos = BORDER, START_Y + line_number * FONT_DY
else:
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
def extract_otps_from_camera(args: Args) -> Otps:
if verbose: print("Capture QR codes from camera")
otp_urls: OtpUrls = []
@@ -289,42 +325,6 @@ def extract_otps_from_camera(args: Args) -> Otps:
return otps
def get_color(new_otps_count: int, otp_url: str) -> ColorBGR:
if new_otps_count:
return SUCCESS_COLOR
else:
if otp_url:
return FAILURE_COLOR
else:
return NORMAL_COLOR
# TODO use cv2 types if available
def cv2_draw_box(img: Any, raw_pts: Any, color: ColorBGR) -> Any:
pts = np.array([raw_pts], np.int32)
pts = pts.reshape((-1, 1, 2))
cv2.polylines(img, [pts], True, color, BOX_THICKNESS)
return pts
# TODO use cv2 types if available
def cv2_print_text(img: Any, text: str, line_number: int, position: TextPosition, color: ColorBGR, opposite_len: Optional[int] = None) -> None:
window_dim = cv2.getWindowImageRect(WINDOW_NAME)
out_text = text
if opposite_len:
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
actual_width = text_dim[TEXT_WIDTH] + opposite_len * CHAR_DX + 4 * BORDER
if actual_width >= window_dim[WINDOW_WIDTH]:
out_text = out_text[:(window_dim[WINDOW_WIDTH] - actual_width) // CHAR_DX] + '.'
text_dim, _ = cv2.getTextSize(out_text, FONT, FONT_SCALE, FONT_THICKNESS)
if position == TextPosition.LEFT:
pos = BORDER, START_Y + line_number * FONT_DY
else:
pos = window_dim[WINDOW_WIDTH] - text_dim[TEXT_WIDTH] - BORDER, START_Y + line_number * FONT_DY
cv2.putText(img, out_text, pos, FONT, FONT_SCALE, color, FONT_THICKNESS, FONT_LINE_STYLE)
def cv2_handle_pressed_keys(qr_mode: QRMode) -> Tuple[bool, QRMode]:
key = cv2.waitKey(1) & 0xFF
quit = False
+4 -3
View File
@@ -2,6 +2,8 @@ from typing import Any
import pytest
from extract_otp_secrets import QRMode
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption("--relaxed", action='store_true', help="run tests in relaxed mode")
@@ -15,7 +17,6 @@ def relaxed(request: pytest.FixtureRequest) -> Any:
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
if "qr_mode" in metafunc.fixturenames:
all_qr_modes = ['ZBAR', 'QREADER', 'QREADER_DEEP', 'CV2', 'CV2_WECHAT']
number = 2 if metafunc.config.getoption("fast") else len(all_qr_modes)
qr_modes = [mode for mode in all_qr_modes]
number = 2 if metafunc.config.getoption("fast") else len(QRMode)
qr_modes = [mode.name for mode in QRMode]
metafunc.parametrize("qr_mode", qr_modes[0:number])
-3
View File
@@ -1,3 +0,0 @@
# comment 1
# comment 2
+1 -103
View File
@@ -26,8 +26,7 @@ import pathlib
import re
import sys
import time
from enum import Enum
from typing import Any, List, Optional, Tuple
from typing import Optional
import colorama
import pytest
@@ -38,12 +37,6 @@ from utils import (count_files_in_dir, file_exits, read_binary_file_as_stream,
import extract_otp_secrets
try:
import cv2 # type: ignore
except ImportError:
# ignore
pass
qreader_available: bool = extract_otp_secrets.qreader_available
@@ -225,18 +218,6 @@ def test_keepass_csv(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path)
assert captured.err == ''
def test_keepass_empty(capsys: pytest.CaptureFixture[str], tmp_path: pathlib.Path) -> None:
# Act
extract_otp_secrets.main(['-k', '-', 'tests/data/only_comments.txt'])
# Assert
captured = capsys.readouterr()
assert captured.out == ''
assert captured.err == ''
def test_keepass_csv_stdout(capsys: pytest.CaptureFixture[str]) -> None:
'''Two csv files .totp and .htop are generated.'''
# Act
@@ -513,89 +494,6 @@ def test_extract_no_arguments(capsys: pytest.CaptureFixture[str], mocker: Mocker
assert e.type == SystemExit
MockMode = Enum('MockMode', ['REPEAT_FIRST_ENDLESS', 'LOOP_LIST'])
class MockCam:
read_counter: int = 0
read_files: List[str] = []
mock_mode: MockMode
def __init__(self, files: List[str] = ['example_export.png'], mock_mode: MockMode = MockMode.REPEAT_FIRST_ENDLESS):
self.read_files = files
self.image_mode = mock_mode
def read(self) -> Tuple[bool, Any]:
if self.image_mode == MockMode.REPEAT_FIRST_ENDLESS:
file = self.read_files[0]
elif self.image_mode == MockMode.LOOP_LIST:
file = self.read_files[self.read_counter]
self.read_counter += 1
if file:
img = cv2.imread(file)
return True, img
else:
return False, None
def release(self) -> None:
# ignore
pass
@pytest.mark.parametrize("qr_reader", [
None,
'ZBAR',
'QREADER',
'QREADER_DEEP',
'CV2',
'CV2_WECHAT'
])
def test_extract_otps_from_camera(qr_reader: Optional[str], capsys: pytest.CaptureFixture[str], mocker: MockerFixture) -> None:
if qreader_available:
# Arrange
mockCam = MockCam()
mocker.patch('cv2.VideoCapture', return_value=mockCam)
mocker.patch('cv2.namedWindow')
mocker.patch('cv2.rectangle')
mocker.patch('cv2.polylines')
mocker.patch('cv2.imshow')
mocker.patch('cv2.getTextSize', return_value=([8, 200], False))
mocker.patch('cv2.putText')
mocker.patch('cv2.getWindowImageRect', return_value=[0, 0, 640, 480])
mocker.patch('cv2.waitKey', return_value=27)
mocker.patch('cv2.getWindowProperty', return_value=False)
mocker.patch('cv2.destroyAllWindows')
args = []
if qr_reader:
args.append('-Q')
args.append(qr_reader)
# Act
extract_otp_secrets.main(args)
# Assert
captured = capsys.readouterr()
assert captured.out == EXPECTED_STDOUT_FROM_EXAMPLE_EXPORT_PNG
assert captured.err == ''
else:
# Act
with pytest.raises(SystemExit) as e:
extract_otp_secrets.main([])
# Assert
captured = capsys.readouterr()
expected_err_msg = 'error: the following arguments are required: infile'
assert expected_err_msg in captured.err
assert captured.out == ''
assert e.value.code == 2
assert e.type == SystemExit
def test_verbose_and_quiet(capsys: pytest.CaptureFixture[str]) -> None:
with pytest.raises(SystemExit) as e:
# Act