~gpanders/passage

ref: 849cef24e44e6b1979a78f9244c02a329a8ed634 passage/src/main.rs -rw-r--r-- 8.5 KiB
849cef24Gregory Anders Add uninstall target to Makefile 11 months 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
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
use clap::{App, AppSettings, Arg, SubCommand};
use std::env;
use std::path::PathBuf;
use std::process;

mod cmd;
mod crypt;
mod error;
mod input;
mod key;
mod store;

use store::PasswordStore;

fn main() {
    let dir = env::var("PASSAGE_STORE_DIR")
        .map(PathBuf::from)
        .unwrap_or_else(|_| dirs::home_dir().unwrap().join(".passage"));
    let store = PasswordStore::new(dir);

    let matches = App::new(clap::crate_name!())
        .version(clap::crate_version!())
        .setting(AppSettings::ArgsNegateSubcommands)
        .setting(AppSettings::VersionlessSubcommands)
        .about("Password store built on the age encryption library")
        .long_about(
            "
passage is a UNIX-style password management tool that allows you to combine password and secrets
management with other programs.

To get started, run 'passage init' to initialize your password store and generate a secret key.
Then use 'passage insert ITEM' and 'passage show ITEM' to insert and retrieve passwords from your
store.

Use 'passage help <SUBCOMMAND>' for more information on how to use each subcommand.
",
        )
        .arg(
            Arg::with_name("clip")
                .short("c")
                .long("clip")
                .requires("item")
                .hidden(true),
        )
        .arg(
            Arg::with_name("item")
                .help("Display password for NAME")
                .value_name("NAME"),
        )
        .subcommand(
            SubCommand::with_name("edit")
                .about("Edit an existing item in the password store")
                .arg(Arg::with_name("item").value_name("NAME").required(true)),
        )
        .subcommand(
            SubCommand::with_name("init")
                .about("Initialize a password store")
                .long_about(
                    "
If a password store does not already exist, and the -k/--key flag is not provided, a new secret key
will be auto-generated.

With -k/--key, the provided secret key is used for the password store instead. If a secret key
already exists, the store is reencrypted to use the new key.

The public key corresponding to your store's secret key is automatically added to the recipients
list in your store. You can add additional recipients to your store using the -r/--recipient flag.
The argument to this flag should be an age public key.

Examples:

Initialize a new store:

    $ passage init

Initialize a new store or reinitialize an existing store using an existing key:

    $ age-keygen -o key.txt
    $ passage init -k key.txt

Add other recipients to your store:

    $ passage init -r age1294r5jdje2n2jprxj0avqyvmpsujzlmjt5kla728x5eykgd8cc9skkms53
",
                )
                .arg(
                    Arg::with_name("recipient")
                        .help("Add an additional recipient to the password store")
                        .short("r")
                        .long("recipient")
                        .multiple(true)
                        .number_of_values(1)
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("key")
                        .help("Initialize store with an existing secret key")
                        .short("k")
                        .long("key")
                        .takes_value(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("show")
                .about("Retrieve a password from the store")
                .long_about(
                    "
With no arguments, 'passage show' is equivalent to 'passage ls' or just 'passage'; namely, it lists
the contents of the store.

With an argument, decrypt and display the given item from the store, if it exists. With -c/--clip,
copy the password to the system clipboard instead.
",
                )
                .arg(Arg::with_name("item").value_name("NAME"))
                .arg(
                    Arg::with_name("clip")
                        .help("Copy password to the system clipboard")
                        .short("c")
                        .long("clip")
                        .requires("item"),
                ),
        )
        .subcommand(
            SubCommand::with_name("ls")
                .alias("list")
                .about("Display the contents of your password store")
                .long_about(
                    "
Displays the directory tree of the password store.

This command is alternatively called 'list'.
",
                ),
        )
        .subcommand(
            SubCommand::with_name("insert")
                .alias("add")
                .about("Insert a new item into the password store")
                .long_about(
                    "
Create a new item in the password store with the given name. If no argument is given, the user is
prompted for the name of the item to create. If the item already exists in the password store, the
user is prompted to confirm that they wish to overwrite the existing item. To bypass confirmation,
use the -f/--force flag.

The user is then prompted to enter the password for the new item and then asked again to confirm
the password.

This command is alternatively called 'add'.
",
                )
                .arg(Arg::with_name("item").value_name("NAME"))
                .arg(
                    Arg::with_name("force")
                        .help("Don't ask before overwriting an existing item")
                        .short("f")
                        .long("force")
                        .requires("item"),
                ),
        )
        .subcommand(
            SubCommand::with_name("rm")
                .alias("remove")
                .about("Remove an item from the password store")
                .long_about(
                    "
Remove the given item from the password store, if it exists. If no argument is given, the user is
prompted for the name of the item to remove.

The user is prompted to confirm that they wish to remove the given item. To bypass confirmation,
use the -f/--force flag.

This command is alternatively called 'remove'.
",
                )
                .arg(Arg::with_name("item").value_name("NAME"))
                .arg(
                    Arg::with_name("force")
                        .help("Don't ask for confirmation")
                        .short("f")
                        .long("force")
                        .requires("item"),
                ),
        )
        .subcommand(
            SubCommand::with_name("lock")
                .about("Lock the password store")
                .long_about(
                    "
Locks the password store by encrypting the secret key with a passphrase. While locked, any time
a password is retrieved from the store (using 'passage show'), the passphrase must be entered.

This can be used to provide an additional measure of security when using systems where other
users have root access.
",
                ),
        )
        .subcommand(
            SubCommand::with_name("unlock")
                .about("Unlock the password store")
                .long_about(
                    "
Unlock the password store by decrypting the secret key.
",
                ),
        )
        .subcommand(
            SubCommand::with_name("pubkey")
                .about("Display the public key corresponding to the password store's secret key"),
        )
        .get_matches();

    let result = match matches.subcommand() {
        ("show", Some(sub)) => match sub.value_of("item") {
            Some(item) => cmd::show(store, item, sub.is_present("clip")),
            None => cmd::list(store),
        },
        ("edit", Some(sub)) => cmd::edit(store, sub.value_of("item")),
        ("init", Some(sub)) => {
            let recipients = sub
                .values_of("recipient")
                .map(|v| v.map(|s| s.parse()).filter_map(|r| r.ok()).collect());
            cmd::init(store, recipients, sub.value_of("key").map(|s| s.to_owned()))
        }
        ("ls", Some(_)) => cmd::list(store),
        ("lock", Some(_)) => cmd::lock(),
        ("unlock", Some(_)) => cmd::unlock(),
        ("pubkey", Some(_)) => cmd::pubkey(),
        ("insert", Some(sub)) => cmd::insert(store, sub.value_of("item"), sub.is_present("force")),
        ("rm", Some(sub)) => cmd::remove(store, sub.value_of("item"), sub.is_present("force")),
        ("", None) => match matches.value_of("item") {
            Some(item) => cmd::show(store, item, matches.is_present("clip")),
            None => cmd::list(store),
        },
        _ => unreachable!(),
    };

    if let Err(e) = result {
        eprintln!("Error: {}", e);
        process::exit(1);
    }
}