~sircmpwn/pass-rotate

ref: 5155c94211aea768630aa98ccb7363d1ae900134 pass-rotate/pass-rotate -rwxr-xr-x 4.2 KiB
5155c942Drew DeVault Merge pull request #38 from AbstractBeliefs/xdg-config-home 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
128
129
130
131
132
#!/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()

if os.environ.get("XDG_CONFIG_HOME"):
    xdg_config_location = os.path.join(os.environ.get("XDG_CONFIG_HOME"), "pass-rotate.ini")
else:
    xdg_config_location = None
cfg_path = args.get("--config") or xdg_config_location 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))
    pass_name = cfg["pass-name"] if "pass-name" in cfg else account
    if not provider:
        print("Error: pass-rotate does not have a service provider for {}".format(domain))
        errs += 1
        continue
    sys.stderr.write("Rotating {}... ".format(pass_name))
    sys.stderr.flush()
    try:
        old_password = get_password(pass_name)
        provider.prepare(old_password)
        new_password = gen_password(pass_name)
        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)