Guest User

Untitled

a guest
Nov 3rd, 2017
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.72 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. # Dependencies: python-tldextract, pass
  4.  
  5. import argparse
  6. import enum
  7. import fnmatch
  8. import functools
  9. import os
  10. import re
  11. import shlex
  12. import subprocess
  13. import sys
  14.  
  15. import tldextract
  16.  
  17. PASSWORD_STORE_PATH = os.path.expanduser('~/.password-store')
  18. DMENU_INVOCATION = 'rofi -dmenu'
  19. ENCODING = 'UTF-8'
  20.  
  21. argument_parser = argparse.ArgumentParser()
  22. argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
  23. argument_parser.add_argument('--password-store', '-p', default=PASSWORD_STORE_PATH)
  24. argument_parser.add_argument('--username-pattern', '-u', default=r'.*/(.+)')
  25. argument_parser.add_argument('--username-target', '-U', choices=['path', 'secret'], default='path')
  26. argument_parser.add_argument('--password-pattern', '-P', default=r'(.*)')
  27. argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu')
  28.  
  29. stderr = functools.partial(print, file=sys.stderr)
  30.  
  31.  
  32. class ExitCodes(enum.IntEnum):
  33. SUCCESS = 0
  34. COULD_NOT_DETERMINE_URL = 1
  35. COULD_NOT_DETERMINE_DOMAIN = 2
  36. NO_PASS_CANDIDATES = 3
  37. COULD_NOT_MATCH_USERNAME = 4
  38. COULD_NOT_MATCH_PASSWORD = 5
  39.  
  40.  
  41. def qute_command(command):
  42. with open(os.environ['QUTE_FIFO'], 'w') as fifo:
  43. fifo.write(f'{command}\n')
  44. fifo.flush()
  45.  
  46.  
  47. def find_pass_candidates(domain, password_store_path=PASSWORD_STORE_PATH):
  48. candidates = []
  49. for path, directories, file_names in os.walk(password_store_path):
  50. if directories or domain not in path.split(os.path.sep):
  51. continue
  52.  
  53. # Strip password store path prefix to get the relative pass path
  54. pass_path = path[len(password_store_path) + 1:]
  55. secrets = fnmatch.filter(file_names, '*.gpg')
  56. candidates.extend(os.path.join(pass_path, os.path.splitext(secret)[0]) for secret in secrets)
  57. return candidates
  58.  
  59.  
  60. def pass_(path):
  61. process = subprocess.run(['pass', path], stdout=subprocess.PIPE, encoding=ENCODING)
  62. return process.stdout.strip()
  63.  
  64.  
  65. def dmenu(items, invocation=DMENU_INVOCATION):
  66. command = shlex.split(invocation)
  67. process = subprocess.run(command, input='\n'.join(items), stdout=subprocess.PIPE, encoding=ENCODING)
  68. return process.stdout.strip()
  69.  
  70.  
  71. def main(arguments):
  72. # Domain wasn't overriden using CLI argument or found in qutebrowser environment variable
  73. if not arguments.url:
  74. stderr('Could not determine URL!')
  75. return ExitCodes.COULD_NOT_DETERMINE_URL
  76.  
  77. domain = tldextract.extract(arguments.url).registered_domain
  78. if not domain:
  79. stderr(f'Could not determine domain from URL: {arguments.url!r}!')
  80. return ExitCodes.COULD_NOT_DETERMINE_DOMAIN
  81.  
  82. candidates = find_pass_candidates(domain, arguments.password_store)
  83. if not candidates:
  84. stderr(f'No candidates for domain {domain!r} found!')
  85. return ExitCodes.NO_PASS_CANDIDATES
  86.  
  87. selection = candidates[0] if len(candidates) == 1 else dmenu(candidates, arguments.dmenu_invocation)
  88. secret = pass_(selection)
  89. data = selection if arguments.username_target == 'path' else secret
  90. match = re.match(arguments.username_pattern, data)
  91. if not match:
  92. stderr(f'Failed to match username pattern on {arguments.username_target}!')
  93. return ExitCodes.COULD_NOT_MATCH_USERNAME
  94. username = match.group(1)
  95.  
  96. match = re.match(arguments.password_pattern, secret)
  97. if not match:
  98. stderr('Failed to match password pattern on secret!')
  99. return ExitCodes.COULD_NOT_MATCH_PASSWORD
  100.  
  101. password = match.group(1)
  102. # Enter back into insert-mode so the user can immediately continue working
  103. qute_command(f'fake-key {username} ;; fake-key <Tab> ;; fake-key {password} ;; enter-mode insert')
  104. return ExitCodes.SUCCESS
  105.  
  106.  
  107. if __name__ == '__main__':
  108. arguments = argument_parser.parse_args()
  109. sys.exit(main(arguments))
Add Comment
Please, Sign In to add comment