docker remove python3-opencv, but add required libs; add debug mode
This commit is contained in:
@@ -15,9 +15,10 @@ COPY . .
|
|||||||
ARG RUN_TESTS=true
|
ARG RUN_TESTS=true
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
|
libgl1 \
|
||||||
|
libglib2.0-0 \
|
||||||
libsm6 \
|
libsm6 \
|
||||||
libzbar0 \
|
libzbar0 \
|
||||||
python3-opencv \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
&& pip install --no-cache-dir -U -r \
|
&& pip install --no-cache-dir -U -r \
|
||||||
requirements.txt \
|
requirements.txt \
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -29,9 +29,13 @@ This script/project was renamed from extract_otp_secret_keys to extract_otp_secr
|
|||||||
git clone https://github.com/scito/extract_otp_secrets.git
|
git clone https://github.com/scito/extract_otp_secrets.git
|
||||||
cd extract_otp_secrets
|
cd extract_otp_secrets
|
||||||
pip install -U -r requirements.txt
|
pip install -U -r requirements.txt
|
||||||
|
|
||||||
|
python src/extract_otp_secrets.py example_export.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
### Installation of shared libraries for ZBAR QR reader
|
In case this script is not starting properly, the debug mode can be activated by adding parameter `-d` in the command line.
|
||||||
|
|
||||||
|
### Installation of shared system libraries
|
||||||
|
|
||||||
For reading QR codes with `ZBAR` QR reader, the zbar library must be installed.
|
For reading QR codes with `ZBAR` QR reader, the zbar library must be installed.
|
||||||
If you do not use the `ZBAR` QR reader, you do not need to install the zbar shared library. Note: The `ZBAR` QR reader is the showed for me the best results and is thus default QR Reader.
|
If you do not use the `ZBAR` QR reader, you do not need to install the zbar shared library. Note: The `ZBAR` QR reader is the showed for me the best results and is thus default QR Reader.
|
||||||
@@ -56,7 +60,13 @@ For a detailed installation documentation of [pyzbar](https://github.com/Natural
|
|||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
The zbar DLLs are included with the Windows Python wheels. However, you might need additionally to install [Visual C++ Redistributable Packages for Visual Studio 2013](https://www.microsoft.com/en-US/download/details.aspx?id=40784). Install `vcredist_x64.exe` if using 64-bit Python, `vcredist_x86.exe` if using 32-bit Python.
|
##### zbar
|
||||||
|
|
||||||
|
The zbar DLLs are included with the Windows Python wheels. However, you might need additionally to install [Visual C++ Redistributable Packages for Visual Studio 2013](https://www.microsoft.com/en-US/download/details.aspx?id=40784). Install `vcredist_x64.exe` if using 64-bit Python, `vcredist_x86.exe` if using 32-bit Python. For more information see [pyzbar](https://github.com/NaturalHistoryMuseum/pyzbar)
|
||||||
|
|
||||||
|
##### OpenCV
|
||||||
|
|
||||||
|
OpenCV requires [Visual C++ redistributable 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48145). For more information see [opencv-python](https://pypi.org/project/opencv-python/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -94,7 +104,9 @@ The zbar DLLs are included with the Windows Python wheels. However, you might ne
|
|||||||
|
|
||||||
## Program help: arguments and options
|
## Program help: arguments and options
|
||||||
|
|
||||||
<pre>Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
|
<pre>usage: extract_otp_secrets.py [-h] [--csv FILE] [--keepass FILE] [--json FILE] [--printqr] [--saveqr DIR] [--camera NUMBER] [--qr {ZBAR,QREADER,QREADER_DEEP,CV2,CV2_WECHAT}] [-i] [--no-color] [-d | -v | -q] [infile ...]
|
||||||
|
|
||||||
|
Extracts one time password (OTP) secrets from QR codes exported by two-factor authentication (2FA) apps
|
||||||
If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
|
If no infiles are provided, a GUI window starts and QR codes are captured from the camera.
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
@@ -113,6 +125,7 @@ options:
|
|||||||
QR reader (default: ZBAR)
|
QR reader (default: ZBAR)
|
||||||
-i, --ignore ignore duplicate otps
|
-i, --ignore ignore duplicate otps
|
||||||
--no-color, -n do not use ANSI colors in console output
|
--no-color, -n do not use ANSI colors in console output
|
||||||
|
-d, --debug enter debug mode, do checks and quit
|
||||||
-v, --verbose verbose output
|
-v, --verbose verbose output
|
||||||
-q, --quiet no stdout output, except output set by -
|
-q, --quiet no stdout output, except output set by -
|
||||||
|
|
||||||
@@ -460,6 +473,7 @@ docker build . -t extract_otp_secrets_only_txt --pull -f Dockerfile_only_txt --b
|
|||||||
```
|
```
|
||||||
|
|
||||||
Run tests in docker container:
|
Run tests in docker container:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
|
docker run --entrypoint /extract/run_pytest.sh --rm -v "$(pwd)":/files:ro extract_otp_secrets
|
||||||
```
|
```
|
||||||
|
|||||||
98
build.sh
98
build.sh
@@ -230,30 +230,6 @@ cmd="$PIP install --use-pep517 -U -r requirements-dev.txt"
|
|||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
cmd="$PIP install -U pipenv"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
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"
|
|
||||||
|
|
||||||
$PIPENV --version
|
|
||||||
|
|
||||||
cmd="$PIPENV update && $PIPENV --rm && $PIPENV install"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
$PIPENV run python --version
|
|
||||||
|
|
||||||
# Lint
|
# Lint
|
||||||
|
|
||||||
LINT_OUT_FILE="tests/reports/flake8_results.txt"
|
LINT_OUT_FILE="tests/reports/flake8_results.txt"
|
||||||
@@ -278,7 +254,54 @@ cmd="$MYPY --strict src/*.py tests/*.py | tee $TYPE_CHECK_OUT_FILE"
|
|||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
# pip install
|
# Test
|
||||||
|
|
||||||
|
cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
cmd="$PYTHON src/extract_otp_secrets.py - < example_export.txt"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
COVERAGE_OUT_FILE="tests/reports/pytest-coverage.txt"
|
||||||
|
cmd="pytest --cov=extract_otp_secrets_test --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT_FILE"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
# Pipenv
|
||||||
|
|
||||||
|
cmd="$PIP install -U pipenv"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
$PIPENV --version
|
||||||
|
|
||||||
|
cmd="$PIPENV update && $PIPENV --rm && $PIPENV install"
|
||||||
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
|
|
||||||
|
$PIPENV run python --version
|
||||||
|
|
||||||
|
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 ."
|
cmd="$PIP install -U -e ."
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
@@ -300,25 +323,6 @@ if $generate_result_files; then
|
|||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Test
|
|
||||||
|
|
||||||
cmd="$PYTHON src/extract_otp_secrets.py example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="$PYTHON src/extract_otp_secrets.py - < example_export.txt"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
COVERAGE_OUT_FILE="tests/reports/pytest-coverage.txt"
|
|
||||||
cmd="pytest --cov=extract_otp_secrets_test --junitxml=tests/reports/pytest.xml --cov-report html:tests/reports/html --cov-report=term-missing tests/ | tee $COVERAGE_OUT_FILE"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
cmd="$PIPENV run pytest --cov=extract_otp_secrets_test tests/"
|
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
|
||||||
eval "$cmd"
|
|
||||||
|
|
||||||
# Update Code Coverage in README.md
|
# Update Code Coverage in README.md
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/pytest-coverage-comment
|
# https://github.com/marketplace/actions/pytest-coverage-comment
|
||||||
@@ -356,7 +360,7 @@ if $build_docker; then
|
|||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
cmd="cat mple_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - -c - > example_output.csv"
|
cmd="cat example_export.txt | docker run --rm -i -v \"$(pwd)\":/files:ro extract_otp_secrets - -c - > example_output.csv"
|
||||||
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
@@ -373,10 +377,10 @@ if $build_docker; then
|
|||||||
eval "$cmd"
|
eval "$cmd"
|
||||||
|
|
||||||
if $run_gui; then
|
if $run_gui; then
|
||||||
cmd="docker run --pull always --rm -v "$(pwd)":/files:ro --device=\"/dev/video0:/dev/video0\" --env=\"DISPLAY\" -v /tmp/.X11-unix:/tmp/.X11-unix:ro extract_otp_secrets &"
|
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
|
if $interactive ; then askContinueYn "$cmd"; else echo -e "${cyan}$cmd${reset}";fi
|
||||||
|
eval "$cmd"
|
||||||
fi
|
fi
|
||||||
eval "$cmd"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if $run_gui; then
|
if $run_gui; then
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ from qrcode import QRCode # type: ignore
|
|||||||
import protobuf_generated_python.google_auth_pb2 as pb
|
import protobuf_generated_python.google_auth_pb2 as pb
|
||||||
import colorama
|
import colorama
|
||||||
|
|
||||||
|
debug_mode = '-d' in sys.argv[1:] or '--debug' in sys.argv[1:]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import cv2 # type: ignore # TODO use cv2 types if available
|
import cv2 # type: ignore # TODO use cv2 types if available
|
||||||
|
|
||||||
@@ -64,11 +66,12 @@ try:
|
|||||||
import pyzbar.pyzbar as zbar # type: ignore
|
import pyzbar.pyzbar as zbar # type: ignore
|
||||||
from qreader import QReader # type: ignore
|
from qreader import QReader # type: ignore
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise SystemExit(f"""
|
print(f"""
|
||||||
ERROR: Cannot import QReader module. This problem is probably due to the missing zbar shared library.
|
ERROR: Cannot import QReader module. This problem is probably due to the missing zbar shared library.
|
||||||
On Linux and macOS libzbar0 must be installed.
|
On Linux and macOS libzbar0 must be installed.
|
||||||
See in README.md for the installation of the libzbar0.
|
See in README.md for the installation of the libzbar0.
|
||||||
Exception: {e}""")
|
Exception: {e}\n""", file=sys.stderr)
|
||||||
|
raise e
|
||||||
|
|
||||||
# Types
|
# Types
|
||||||
# workaround for PYTHON <= 3.9: Final[tuple[int]]
|
# workaround for PYTHON <= 3.9: Final[tuple[int]]
|
||||||
@@ -102,8 +105,10 @@ Exception: {e}""")
|
|||||||
TextPosition = Enum('TextPosition', ['LEFT', 'RIGHT'])
|
TextPosition = Enum('TextPosition', ['LEFT', 'RIGHT'])
|
||||||
|
|
||||||
qreader_available = True
|
qreader_available = True
|
||||||
except ImportError:
|
except ImportError as e:
|
||||||
qreader_available = False
|
qreader_available = False
|
||||||
|
if debug_mode:
|
||||||
|
raise e
|
||||||
|
|
||||||
# Workaround for PYTHON <= 3.9: Union[int, None] used instead of int | None
|
# Workaround for PYTHON <= 3.9: Union[int, None] used instead of int | None
|
||||||
|
|
||||||
@@ -150,6 +155,9 @@ def main(sys_args: list[str]) -> None:
|
|||||||
if colored:
|
if colored:
|
||||||
colorama.just_fix_windows_console()
|
colorama.just_fix_windows_console()
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
sys.exit(0 if do_debug_checks() else 1)
|
||||||
|
|
||||||
otps = extract_otps(args)
|
otps = extract_otps(args)
|
||||||
write_csv(args, otps)
|
write_csv(args, otps)
|
||||||
write_keepass_csv(args, otps)
|
write_keepass_csv(args, otps)
|
||||||
@@ -184,15 +192,19 @@ b) image file containing a QR code or = for stdin for an image containing a QR c
|
|||||||
arg_parser.add_argument('-i', '--ignore', help='ignore duplicate otps', action='store_true')
|
arg_parser.add_argument('-i', '--ignore', help='ignore duplicate otps', action='store_true')
|
||||||
arg_parser.add_argument('--no-color', '-n', help='do not use ANSI colors in console output', action='store_true')
|
arg_parser.add_argument('--no-color', '-n', help='do not use ANSI colors in console output', action='store_true')
|
||||||
output_group = arg_parser.add_mutually_exclusive_group()
|
output_group = arg_parser.add_mutually_exclusive_group()
|
||||||
|
output_group.add_argument('-d', '--debug', help='enter debug mode, do checks and quit', action='count')
|
||||||
output_group.add_argument('-v', '--verbose', help='verbose output', action='count')
|
output_group.add_argument('-v', '--verbose', help='verbose output', action='count')
|
||||||
output_group.add_argument('-q', '--quiet', help='no stdout output, except output set by -', action='store_true')
|
output_group.add_argument('-q', '--quiet', help='no stdout output, except output set by -', action='store_true')
|
||||||
args = arg_parser.parse_args(sys_args)
|
args = arg_parser.parse_args(sys_args)
|
||||||
|
colored = not args.no_color
|
||||||
if args.csv == '-' or args.json == '-' or args.keepass == '-':
|
if args.csv == '-' or args.json == '-' or args.keepass == '-':
|
||||||
args.quiet = args.q = True
|
args.quiet = args.q = True
|
||||||
|
|
||||||
verbose = args.verbose if args.verbose else LogLevel.NORMAL
|
verbose = args.verbose if args.verbose else LogLevel.NORMAL
|
||||||
|
if args.debug:
|
||||||
|
verbose = LogLevel.DEBUG
|
||||||
|
log_debug('Debug mode start')
|
||||||
quiet = True if args.quiet else False
|
quiet = True if args.quiet else False
|
||||||
colored = not args.no_color
|
|
||||||
if verbose: print(f"QReader installed: {qreader_available}")
|
if verbose: print(f"QReader installed: {qreader_available}")
|
||||||
if qreader_available:
|
if qreader_available:
|
||||||
if verbose >= LogLevel.VERBOSE: print(f"CV2 version: {cv2.__version__}")
|
if verbose >= LogLevel.VERBOSE: print(f"CV2 version: {cv2.__version__}")
|
||||||
@@ -706,6 +718,16 @@ def next_qr_mode(qr_mode: QRMode) -> QRMode:
|
|||||||
return QRMode((qr_mode.value + 1) % len(QRMode))
|
return QRMode((qr_mode.value + 1) % len(QRMode))
|
||||||
|
|
||||||
|
|
||||||
|
def do_debug_checks() -> bool:
|
||||||
|
log_debug('Do debug checks')
|
||||||
|
log_debug('Try: import cv2')
|
||||||
|
import cv2 # noqa: F401 # This is only a debug import
|
||||||
|
log_debug('Try: import numpy as np')
|
||||||
|
import numpy as np # noqa: F401 # This is only a debug import
|
||||||
|
print(color('\nDebug checks passed', colorama.Fore.GREEN))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
# workaround for PYTHON <= 3.9 use: BaseException | None
|
# workaround for PYTHON <= 3.9 use: BaseException | None
|
||||||
def log_debug(*values: object, sep: Optional[str] = ' ') -> None:
|
def log_debug(*values: object, sep: Optional[str] = ' ') -> None:
|
||||||
if colored:
|
if colored:
|
||||||
|
|||||||
Reference in New Issue
Block a user