mirror of
https://github.com/scito/extract_otp_secret_keys.git
synced 2025-12-06 23:05:06 +01:00
refactor to satisfy flak8
This commit is contained in:
parent
7f8250c0a6
commit
175e7c38e5
5 changed files with 37 additions and 8 deletions
8
.flake8
Normal file
8
.flake8
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
E701
|
||||||
|
exclude =
|
||||||
|
protobuf_generated_python
|
||||||
|
__pycache__
|
||||||
|
per-file-ignores =
|
||||||
|
extract_otp_secret_keys.py: F821, F401
|
||||||
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
pytest
|
pytest
|
||||||
|
|
|
||||||
|
|
@ -49,17 +49,20 @@ import csv
|
||||||
import json
|
import json
|
||||||
from urllib.parse import parse_qs, urlencode, urlparse, quote
|
from urllib.parse import parse_qs, urlencode, urlparse, quote
|
||||||
from os import path, mkdir
|
from os import path, mkdir
|
||||||
from re import sub, compile as rcompile
|
from re import compile as rcompile
|
||||||
import protobuf_generated_python.google_auth_pb2
|
import protobuf_generated_python.google_auth_pb2
|
||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/questions/40226049/find-enums-listed-in-python-descriptor-for-protobuf
|
# https://stackoverflow.com/questions/40226049/find-enums-listed-in-python-descriptor-for-protobuf
|
||||||
def get_enum_name_by_number(parent, field_name):
|
def get_enum_name_by_number(parent, field_name):
|
||||||
field_value = getattr(parent, field_name)
|
field_value = getattr(parent, field_name)
|
||||||
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name
|
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name
|
||||||
|
|
||||||
|
|
||||||
def convert_secret_from_bytes_to_base32_str(bytes):
|
def convert_secret_from_bytes_to_base32_str(bytes):
|
||||||
return str(base64.b32encode(bytes), 'utf-8').replace('=', '')
|
return str(base64.b32encode(bytes), 'utf-8').replace('=', '')
|
||||||
|
|
||||||
|
|
||||||
def save_qr(data, name):
|
def save_qr(data, name):
|
||||||
global verbose
|
global verbose
|
||||||
qr = QRCode()
|
qr = QRCode()
|
||||||
|
|
@ -68,11 +71,13 @@ def save_qr(data, name):
|
||||||
if verbose: print('Saving to {}'.format(name))
|
if verbose: print('Saving to {}'.format(name))
|
||||||
img.save(name)
|
img.save(name)
|
||||||
|
|
||||||
|
|
||||||
def print_qr(data):
|
def print_qr(data):
|
||||||
qr = QRCode()
|
qr = QRCode()
|
||||||
qr.add_data(data)
|
qr.add_data(data)
|
||||||
qr.print_ascii()
|
qr.print_ascii()
|
||||||
|
|
||||||
|
|
||||||
def parse_args(sys_args):
|
def parse_args(sys_args):
|
||||||
arg_parser = argparse.ArgumentParser()
|
arg_parser = argparse.ArgumentParser()
|
||||||
arg_parser.add_argument('--verbose', '-v', help='verbose output', action='store_true')
|
arg_parser.add_argument('--verbose', '-v', help='verbose output', action='store_true')
|
||||||
|
|
@ -88,9 +93,11 @@ def parse_args(sys_args):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def sys_main():
|
def sys_main():
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
def main(sys_args):
|
def main(sys_args):
|
||||||
global verbose, quiet
|
global verbose, quiet
|
||||||
args = parse_args(sys_args)
|
args = parse_args(sys_args)
|
||||||
|
|
@ -102,6 +109,7 @@ def main(sys_args):
|
||||||
write_csv(args, otps)
|
write_csv(args, otps)
|
||||||
write_json(args, otps)
|
write_json(args, otps)
|
||||||
|
|
||||||
|
|
||||||
def extract_otps(args):
|
def extract_otps(args):
|
||||||
global verbose, quiet
|
global verbose, quiet
|
||||||
quiet = args.quiet
|
quiet = args.quiet
|
||||||
|
|
@ -115,7 +123,7 @@ def extract_otps(args):
|
||||||
if not line.startswith('otpauth-migration://'): print('\nWARN: line is not a otpauth-migration:// URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line))
|
if not line.startswith('otpauth-migration://'): print('\nWARN: line is not a otpauth-migration:// URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line))
|
||||||
parsed_url = urlparse(line)
|
parsed_url = urlparse(line)
|
||||||
params = parse_qs(parsed_url.query)
|
params = parse_qs(parsed_url.query)
|
||||||
if not 'data' in params:
|
if 'data' not in params:
|
||||||
print('\nERROR: no data query parameter in input URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line))
|
print('\nERROR: no data query parameter in input URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
data_encoded = params['data'][0]
|
data_encoded = params['data'][0]
|
||||||
|
|
@ -135,7 +143,7 @@ def extract_otps(args):
|
||||||
if otp.issuer and not quiet: print('Issuer: {}'.format(otp.issuer))
|
if otp.issuer and not quiet: print('Issuer: {}'.format(otp.issuer))
|
||||||
otp_type = get_enum_name_by_number(otp, 'type')
|
otp_type = get_enum_name_by_number(otp, 'type')
|
||||||
if not quiet: print('Type: {}'.format(otp_type))
|
if not quiet: print('Type: {}'.format(otp_type))
|
||||||
url_params = { 'secret': secret }
|
url_params = {'secret': secret}
|
||||||
if otp.type == 1: url_params['counter'] = otp.counter
|
if otp.type == 1: url_params['counter'] = otp.counter
|
||||||
if otp.issuer: url_params['issuer'] = otp.issuer
|
if otp.issuer: url_params['issuer'] = otp.issuer
|
||||||
otp_url = 'otpauth://{}/{}?'.format('totp' if otp.type == 2 else 'hotp', quote(otp.name)) + urlencode(url_params)
|
otp_url = 'otpauth://{}/{}?'.format('totp' if otp.type == 2 else 'hotp', quote(otp.name)) + urlencode(url_params)
|
||||||
|
|
@ -143,7 +151,7 @@ def extract_otps(args):
|
||||||
if args.printqr:
|
if args.printqr:
|
||||||
print_qr(otp_url)
|
print_qr(otp_url)
|
||||||
if args.saveqr:
|
if args.saveqr:
|
||||||
if not(path.exists('qr')): mkdir('qr')
|
if not (path.exists('qr')): mkdir('qr')
|
||||||
pattern = rcompile(r'[\W_]+')
|
pattern = rcompile(r'[\W_]+')
|
||||||
file_otp_name = pattern.sub('', otp.name)
|
file_otp_name = pattern.sub('', otp.name)
|
||||||
file_otp_issuer = pattern.sub('', otp.issuer)
|
file_otp_issuer = pattern.sub('', otp.issuer)
|
||||||
|
|
@ -159,6 +167,7 @@ def extract_otps(args):
|
||||||
})
|
})
|
||||||
return otps
|
return otps
|
||||||
|
|
||||||
|
|
||||||
def write_csv(args, otps):
|
def write_csv(args, otps):
|
||||||
global verbose, quiet
|
global verbose, quiet
|
||||||
if args.csv and len(otps) > 0:
|
if args.csv and len(otps) > 0:
|
||||||
|
|
@ -168,12 +177,14 @@ def write_csv(args, otps):
|
||||||
writer.writerows(otps)
|
writer.writerows(otps)
|
||||||
if not quiet: print("Exported {} otps to csv".format(len(otps)))
|
if not quiet: print("Exported {} otps to csv".format(len(otps)))
|
||||||
|
|
||||||
|
|
||||||
def write_json(args, otps):
|
def write_json(args, otps):
|
||||||
global verbose, quiet
|
global verbose, quiet
|
||||||
if args.json:
|
if args.json:
|
||||||
with open(args.json, "w") as outfile:
|
with open(args.json, "w") as outfile:
|
||||||
json.dump(otps, outfile, indent = 4)
|
json.dump(otps, outfile, indent=4)
|
||||||
if not quiet: print("Exported {} otp entries to json".format(len(otps)))
|
if not quiet: print("Exported {} otp entries to json".format(len(otps)))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sys_main()
|
sys_main()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import os
|
||||||
|
|
||||||
import extract_otp_secret_keys
|
import extract_otp_secret_keys
|
||||||
|
|
||||||
|
|
||||||
def test_extract_csv():
|
def test_extract_csv():
|
||||||
# Arrange
|
# Arrange
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
@ -35,11 +36,12 @@ def test_extract_csv():
|
||||||
expected_csv = read_csv('example_output.csv')
|
expected_csv = read_csv('example_output.csv')
|
||||||
actual_csv = read_csv('test_example_output.csv')
|
actual_csv = read_csv('test_example_output.csv')
|
||||||
|
|
||||||
assert actual_csv == actual_csv
|
assert actual_csv == expected_csv
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
||||||
|
|
||||||
def test_extract_json():
|
def test_extract_json():
|
||||||
# Arrange
|
# Arrange
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
@ -55,13 +57,16 @@ def test_extract_json():
|
||||||
# Clean up
|
# Clean up
|
||||||
cleanup()
|
cleanup()
|
||||||
|
|
||||||
|
|
||||||
def cleanup():
|
def cleanup():
|
||||||
remove_file('test_example_output.csv')
|
remove_file('test_example_output.csv')
|
||||||
remove_file('test_example_output.json')
|
remove_file('test_example_output.json')
|
||||||
|
|
||||||
|
|
||||||
def remove_file(filename):
|
def remove_file(filename):
|
||||||
if os.path.exists(filename): os.remove(filename)
|
if os.path.exists(filename): os.remove(filename)
|
||||||
|
|
||||||
|
|
||||||
def read_csv(filename):
|
def read_csv(filename):
|
||||||
"""Returns a list of lines."""
|
"""Returns a list of lines."""
|
||||||
with open(filename, "r") as infile:
|
with open(filename, "r") as infile:
|
||||||
|
|
@ -71,6 +76,7 @@ def read_csv(filename):
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def read_json(filename):
|
def read_json(filename):
|
||||||
"""Returns a list or a dictionary."""
|
"""Returns a list or a dictionary."""
|
||||||
with open(filename, "r") as infile:
|
with open(filename, "r") as infile:
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import os
|
||||||
|
|
||||||
import extract_otp_secret_keys
|
import extract_otp_secret_keys
|
||||||
|
|
||||||
|
|
||||||
class TestExtract(unittest.TestCase):
|
class TestExtract(unittest.TestCase):
|
||||||
|
|
||||||
def test_extract_csv(self):
|
def test_extract_csv(self):
|
||||||
|
|
@ -53,9 +54,11 @@ class TestExtract(unittest.TestCase):
|
||||||
remove_file('test_example_output.csv')
|
remove_file('test_example_output.csv')
|
||||||
remove_file('test_example_output.json')
|
remove_file('test_example_output.json')
|
||||||
|
|
||||||
|
|
||||||
def remove_file(filename):
|
def remove_file(filename):
|
||||||
if os.path.exists(filename): os.remove(filename)
|
if os.path.exists(filename): os.remove(filename)
|
||||||
|
|
||||||
|
|
||||||
def read_csv(filename):
|
def read_csv(filename):
|
||||||
"""Returns a list of lines."""
|
"""Returns a list of lines."""
|
||||||
with open(filename, "r") as infile:
|
with open(filename, "r") as infile:
|
||||||
|
|
@ -65,6 +68,7 @@ def read_csv(filename):
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def read_json(filename):
|
def read_json(filename):
|
||||||
"""Returns a list or a dictionary."""
|
"""Returns a list or a dictionary."""
|
||||||
with open(filename, "r") as infile:
|
with open(filename, "r") as infile:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue