~sircmpwn/pass-rotate

ref: f2b18a3604e7a89bc0399c8fcfbdc06189a23003 pass-rotate/pass-rotate -rwxr-xr-x 3.9 KiB
f2b18a36Drew DeVault Merge pull request #28 from rememberYou/master 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
"""pass-rotate

Usage:
  pass-rotate <accounts>...
  pass-rotate --list-accounts
  pass-rotate --list-providers
  pass-rotate --list-options <provider>

Options:
  --list-accounts   Print all configured accounts
  --list-providers  Print all supported service providers and exit
  --list-options    Prints options for the specified provider and exit
  --config=<file>   Specify an alternate config file (default: ~/.config/pass-rotate.ini)
"""

from passrotate import PassRotate
from configparser import ConfigParser
from docopt import docopt
import traceback
import subprocess
import sys
import os
from getpass import getpass

args = docopt(__doc__, version='pass-rotate 1.0')

pass_rotate = PassRotate()

if args["--list-providers"]:
    providers = sorted(pass_rotate.get_providers(), key=lambda p: p.name)
    print("{:<24} {:<24}\n".format("Domain", "Provider"))
    for p in providers:
        for d in p.domains:
            print("{:<24} {:<24}".format(d, p.name))
    sys.exit()

if args["--list-options"]:
    provider = pass_rotate.get_provider_class(args["<provider>"])
    if not provider:
        print("Unknown provider")
        sys.exit(1)
    print(provider.name)
    print("\nDomains:")
    for d in provider.domains:
        print("    {}".format(d))
    print("\nUsage:")
    if provider.__doc__.startswith("\n"):
        print(provider.__doc__[1:].rstrip())
    else:
        print(provider.__doc__.rstrip())
    sys.exit()

cfg_path = args.get("--config") or os.path.expanduser("~/.config/pass-rotate.ini")
try:
    with open(cfg_path) as f:
        config = ConfigParser()
        config.readfp(f)
except Exception as ex:
    sys.stderr.write(str(ex))
    sys.stderr.write("\nFailed to read config file.\n")
    sys.exit(1)

if args["--list-accounts"]:
    accounts = sorted([
        s for s in config.sections() \
            if s != "pass-rotate" and pass_rotate.get_provider_class(config[s].get("domain") or s)
        ])
    [print(a) for a in accounts]
    sys.exit()

_get_password_cmd = config["pass-rotate"]["get-password"]
_gen_password_cmd = config["pass-rotate"]["gen-password"]

def get_password(account):
    env = os.environ
    env.update({ "ACCOUNT": account })
    subp = subprocess.run([_get_password_cmd],
            shell=True, env=env,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if subp.returncode != 0:
        raise Exception("get_password command exited with nonzero status code")
    return subp.stdout.decode().strip()

def gen_password(account):
    env = os.environ
    env.update({ "ACCOUNT": account })
    subp = subprocess.run([_gen_password_cmd],
            shell=True, env=env,
            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    if subp.returncode != 0:
        raise Exception("gen_password command exited with nonzero status code")
    return get_password(account)

def custom_prompt(prompt, prompt_type):
    return getpass(prompt="\n  " + prompt + ": ")

pass_rotate.set_prompt(custom_prompt)

errs = 0
for account in args.get("<accounts>"):
    if not config.has_section(account):
        print("Error: No account configured for {}".format(account))
        errs += 1
        continue
    cfg = config[account]
    domain = cfg.get("domain") or account
    provider = pass_rotate.get_provider(domain, dict(cfg))
    if not provider:
        print("Error: pass-rotate does not have a service provider for {}".format(domain))
        errs += 1
        continue
    sys.stderr.write("Rotating {}... ".format(account))
    sys.stderr.flush()
    try:
        old_password = get_password(account)
        provider.prepare(old_password)
        new_password = gen_password(account)
        provider.execute(old_password, new_password)
        sys.stderr.write("OK\n")
    except:
        sys.stderr.write("FAIL\n")
        sys.stderr.write(traceback.format_exc())
        sys.stderr.write("\nFailed to rotate {}\n".format(account))
        errs += 1
    sys.stderr.flush()
sys.exit(errs)