forked from jaraco/keyring
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cli.py
174 lines (146 loc) · 4.95 KB
/
cli.py
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
"""Simple command line interface to get/set password from a keyring"""
import argparse
import getpass
import sys
from . import (
backend,
completion,
core,
delete_password,
get_password,
set_keyring,
set_password,
get_credential,
)
from .util import platform_
class CommandLineTool:
def __init__(self):
self.parser = argparse.ArgumentParser()
self.parser.add_argument(
"-p",
"--keyring-path",
dest="keyring_path",
default=None,
help="Path to the keyring backend",
)
self.parser.add_argument(
"-b",
"--keyring-backend",
dest="keyring_backend",
default=None,
help="Name of the keyring backend",
)
self.parser.add_argument(
"--list-backends",
action="store_true",
help="List keyring backends and exit",
)
self.parser.add_argument(
"--disable", action="store_true", help="Disable keyring and exit"
)
self.parser._operations = ["get", "set", "del", "getcreds", "diagnose"]
self.parser.add_argument(
'operation',
choices=self.parser._operations,
nargs="?",
)
self.parser.add_argument(
'service',
nargs="?",
)
self.parser.add_argument(
'username',
nargs="?",
)
completion.install(self.parser)
def run(self, argv):
args = self.parser.parse_args(argv)
vars(self).update(vars(args))
if args.list_backends:
for k in backend.get_all_keyring():
print(k)
return
if args.disable:
core.disable()
return
if args.operation == 'diagnose':
self.diagnose()
return
self._check_args()
self._load_spec_backend()
method = getattr(self, f'do_{self.operation}', self.invalid_op)
return method()
def _check_args(self):
if self.operation:
if self.operation == 'getcreds':
if self.service is None :
self.parser.error(f"{self.operation} requires service")
elif self.service is None or self.username is None:
self.parser.error(f"{self.operation} requires service and username")
def do_get(self):
password = get_password(self.service, self.username)
if password is None:
raise SystemExit(1)
print(password)
def do_getcreds(self):
creds = get_credential(self.service, self.username)
if creds is None:
raise SystemExit(1)
print(f"username: {creds.username}")
print(f"password: {creds.password}")
def do_set(self):
password = self.input_password(
f"Password for '{self.username}' in '{self.service}': "
)
set_password(self.service, self.username, password)
def do_del(self):
delete_password(self.service, self.username)
def diagnose(self):
config_root = core._config_path()
if config_root.exists():
print("config path:", config_root)
else:
print("config path:", config_root, "(absent)")
print("data root:", platform_.data_root())
def invalid_op(self):
self.parser.error(f"Specify operation ({', '.join(self.parser._operations)}).")
def _load_spec_backend(self):
if self.keyring_backend is None:
return
try:
if self.keyring_path:
sys.path.insert(0, self.keyring_path)
set_keyring(core.load_keyring(self.keyring_backend))
except Exception as exc:
# Tons of things can go wrong here:
# ImportError when using "fjkljfljkl"
# AttributeError when using "os.path.bar"
# TypeError when using "__builtins__.str"
# So, we play on the safe side, and catch everything.
self.parser.error(f"Unable to load specified keyring: {exc}")
def input_password(self, prompt):
"""Retrieve password from input."""
return self.pass_from_pipe() or getpass.getpass(prompt)
@classmethod
def pass_from_pipe(cls):
"""Return password from pipe if not on TTY, else False."""
is_pipe = not sys.stdin.isatty()
return is_pipe and cls.strip_last_newline(sys.stdin.read())
@staticmethod
def strip_last_newline(str):
r"""Strip one last newline, if present.
>>> CommandLineTool.strip_last_newline('foo')
'foo'
>>> CommandLineTool.strip_last_newline('foo\n')
'foo'
"""
slc = slice(-1 if str.endswith('\n') else None)
return str[slc]
def main(argv=None):
"""Main command line interface."""
if argv is None:
argv = sys.argv[1:]
cli = CommandLineTool()
return cli.run(argv)
if __name__ == '__main__':
sys.exit(main())