Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.6.0 Release #45

Merged
merged 38 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b198ec9
Bumped version for next dev cycle.
oubiwann Jan 7, 2023
b22ae17
Updated project desc.
oubiwann Jan 7, 2023
845a298
Updated wording in feature list.
oubiwann Jan 7, 2023
93e7e4d
Fixed bug #37.
oubiwann Jan 7, 2023
6dbf033
Added config and logging support.
oubiwann Jan 7, 2023
5257f13
Addeed more logging.
oubiwann Jan 7, 2023
4455dac
Implemented app abstraction.
oubiwann Jan 7, 2023
8263063
Updated the rest of the commands to take the app.
oubiwann Jan 8, 2023
c188258
Cleanup.
oubiwann Jan 8, 2023
30896d1
Moved about DB setup.
oubiwann Jan 9, 2023
07d3180
Added an id to the list result.
oubiwann Jan 9, 2023
9c84de6
Got around db locks.
oubiwann Jan 9, 2023
dcab60c
Added debug export type.
oubiwann Jan 9, 2023
9228daf
Re-added debug export.
oubiwann Jan 9, 2023
e4d5cf3
Added encoding flag and feature.
oubiwann Jan 11, 2023
949a2ae
Bumped description.
oubiwann Jan 11, 2023
fb15e7f
Updated docs.
oubiwann Jan 11, 2023
7ce0233
Moved db setup back down the subcommand stack.
oubiwann Jan 11, 2023
8dd5e11
Added count column.
oubiwann Jan 11, 2023
36d4ba9
Fixed data persistence when listing.
oubiwann Jan 11, 2023
072f8cc
Ooops; this commit is from an older change that was never saved.
oubiwann Jan 11, 2023
5df2a5b
Added 'add' command.
oubiwann Jan 12, 2023
acd7d1c
Added 'update' command.
oubiwann Jan 12, 2023
77233bd
Bumped version for release.
oubiwann Jan 12, 2023
8fd262c
Added missing required.
oubiwann Jan 12, 2023
a6d7771
Updated usage.
oubiwann Jan 12, 2023
eabc26c
Updated completed feature listing.
oubiwann Jan 12, 2023
38ddbe3
Default password changes.
oubiwann Jan 12, 2023
7579a6b
Updated docs in preparation for #46.
oubiwann Jan 12, 2023
ac4fd21
Updated feature description.
oubiwann Jan 12, 2023
9ac2021
Updated feature list with version and links.
oubiwann Jan 12, 2023
1ec12f2
More link updates.
oubiwann Jan 12, 2023
78698c8
First steps in 'set' with subcommsnds (and associated refactor).
oubiwann Jan 13, 2023
c9d8172
Cleanup.
oubiwann Jan 13, 2023
7e15073
Implemented 'set type'.
oubiwann Jan 13, 2023
3da9ec6
Implemented 'set url'.
oubiwann Jan 13, 2023
bf7409d
Implemented 'set user'.
oubiwann Jan 13, 2023
127ef3f
Updated docs.
oubiwann Jan 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rucksack"
description = "A terminal-based password manager, generator, and importer (Firefox, Chrome)"
version = "0.5.0"
description = "A terminal-based password manager, generator, and importer/exporter (Firefox, Chrome) backed with a concurrent hashmap"
version = "0.6.0"
license = "Apache-2.0"
authors = ["Duncan McGreggor <oubiwann@gmail.com>"]
repository = "https://github.com/oxur/rucksack"
Expand All @@ -18,21 +18,24 @@ name = "rucksack"
aead = "0.5.1"
aes-gcm = "0.10.1"
anyhow = "1.0"
base64 = "0.21"
bincode = { version = "2.0.0-rc.2", features = ["serde"] }
chrono = "0.4.23"
clap = "4.0.32"
clap = { version = "4.0.32", features = ["string"] }
clap_complete = "4.0"
confyg = "0.1.3"
crc32fast = "1.3.2"
csv = "1.1.6"
dashmap = { version = "5.4.0", features = ["serde"] }
lipsum = "0.8.2"
log = "0.4.17"
passwords = "3.1.12"
rand = "0.8"
rpassword = "7.1"
secrecy = "0.8.0"
serde = { version = "1.0", features = ["derive"] }
shellexpand = "3.0.0"
twyg = "0.1.10"
url = "2.3.1"
uuid = {version = "1.2.2", features = ["v4"]}

Expand Down
125 changes: 109 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@

[![][logo]][logo-large]

*A terminal-based password manager, generator, and importer (Firefox, Chrome)*
*A terminal-based password manager, generator, and importer/exporter (Firefox, Chrome) backed with a concurrent hashmap*

## Features

* [x] Password generator
* [x] Encrypted local storage
* [x] Concurrent hashmap for use by daemons
* [x] Supports Firefox Sync
* [x] List secrets (encrypted and decrypted)
* [x] Searching secrets (filtering)
* [x] Reports (quality, duplicates, etc.)
* [ ] Add new records to the DB via CLI subcommand
* [ ] Local network sync
* [x] Password generator (0.1.0)
* [x] Encrypted local storage (0.2.0)
* [x] Concurrent hashmap for use by daemons (0.2.0)
* [x] List secrets, both encrypted and decrypted (0.3.0)
* [x] Supports Firefox and Chrome CSV formats (for importing, 0.3.0 and exporting, 0.5.0)
* [x] Searching secrets via filtering (0.4.0)
* [x] Reports on password quality, duplicates, etc. (0.5.0)
* [x] Add new records to the DB (and support updates) via CLI subcommands (0.6.0)
* [ ] [Database restores](https://github.com/oxur/rucksack/milestone/9)
* [ ] [Local network sync](https://github.com/oxur/rucksack/milestone/10)
* [ ] [Firefox Account Client Syncing](https://github.com/oxur/rucksack/milestone/11)

## Usage

Expand Down Expand Up @@ -61,28 +63,117 @@ New password: Esse-maius-amicitia,-nihil.-]9^,
Password score: 100.00
```

Some systems can't handle special characters, so a flag is available for encoding with base64, with the generated encoding getting scored:

```shell
./bin/rucksack gen --type lipsum --encode

New password: VmVydW0sLW9waW5vciwtc2NyaXB0b3JlbS10YW1lbi4tLjYrfQ
Password score: 100.00
```

### Importing and Exporting

Import login data from Firefox Sync:

```shell
./bin/rucksack import \
--db-pass abc123 \
--type firefox \
--password abc123 \
--file ~/Downloads/logins.csv
```

Logins may be exported to files that can then be used to import into browsers:

```shell
./bin/rucksack export \
--db-pass abc123 \
--type chrome \
--password abc123 \
--file /tmp/exported-logins.csv
```

For both importing and exporting, there are currently two supported types: `firefox` and `chrome`.

### Adding and Updating via Command

To add a single record via the CLI:

```shell
./bin/rucksack add \
--url http://example.com \
--user shelly \
--password whyyyyyy
```

Note that `--user` and `--url` are required when adding a new record. A password is required, too: if one is not provided with `--password`, then you will be prompted:

```shell
./bin/rucksack add \
--url http://example.com \
--user shelly
```

```shell
Enter db password:
```

```shell
Enter password for record:
```

There are several types of changes to records that can't be made via an "update" subcommand due to how the data is used in the database. That did't leave too much data left for an "update" command, so the "record type" update was moved into the "set" group, too. The total list of `set` operations is:

* changing the password
* changing the user (account name)
* changing the URL
* changing the type of record

As such, these have their own sub commands (under `set`), as well as their flags and logic.

Changing a password:

```shell
./bin/rucksack set password \
--url http://example.com \
--user shelly
--password whyyyyyyyyyyyyyyyyyyy
```

If the password isn't provided, you will be prompted at the terminal:

```shell
Enter account password:
```

Changing a user:

```shell
./bin/rucksack set user \
--url http://example.com \
--old-user shelly
--new-user clammy
```

Changing a URL:

```shell
./bin/rucksack set url \
--old-url http://example.com \
--new-url http://shelly.com \
--user clammy
```

Changing the record type:

```shell
./bin/rucksack set type \
--url http://example.com \
--user clammy
--type account
```

Note that for all of this, should you want to pass the DB pass, file, or salt, you will need to make sure those flags come after `set` but before the following subcommmand.

### List Secrets

Show URL/accounts for all secrets:
Expand All @@ -98,26 +189,28 @@ Enter db password:
Show URLs, accounts, passwords, and password scores for all secrets:

```shell
./bin/rucksack list --db --decrypt
./bin/rucksack list --decrypt
```

```shell
Enter db password:
```

In both cases a password may be passed with the `--password` flag. By default, the salt is the value of the `USER` environment variable; it may be overridden with `--salt`.
In both cases a password may be passed with the `--db-pass` flag. By default, the salt is the value of the `USER` environment variable, but it may be overridden with the `--salt` flag.

Note that without `--decrypt`, only the user and URL are displayed. With `--decrypt`, those as well as masked password and password score are displayed. To unmask the password, one must also set `--reveal`.

The default database location used is `./data/creds.db`. To use another location, the `--db` flag is available.

The flags `--db`, `--db-pass`, and `--salt` may be set for any subcommand that access the database.

### Search / Filter Secrets

Simple filtering is also possible (done using a flag with the `list` command, with or without sorting):

```shell
./bin/rucksack list \
--password abc123 \
--db-pass abc123 \
--filter exa \
--sort-by score \
--decrypt
Expand Down Expand Up @@ -149,7 +242,7 @@ For use in auditing, sites+user combinations that share the same password can be

```shell
./bin/rucksack list \
--group-by password \
--group-by db-pass \
--decrypt
```

Expand Down
4 changes: 4 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[logging]
coloured = true
level = "debug"
report_caller = true
7 changes: 7 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::{config, store};

#[derive(Debug)]
pub struct App {
pub cfg: config::Config,
pub db: store::db::DB,
}
36 changes: 36 additions & 0 deletions src/cli/command/add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use anyhow::{anyhow, Result};
use clap::ArgMatches;

use super::util;

use crate::app::App;
use crate::store::{Creds, DecryptedRecord, Metadata};
use crate::time;

pub fn new(matches: &ArgMatches, app: &App) -> Result<()> {
log::debug!("Running 'add' subcommand ...");
if let Ok(_dr) = util::record(&app.db, matches) {
return Err(anyhow!(
"Record already exists -- please use the 'update' command"
));
}
let now = time::now();
let creds = Creds {
user: util::user(matches),
password: util::account_pwd_revealed(matches),
};
let metadata = Metadata {
kind: util::account_kind(matches),
url: util::url(matches),
created: now.clone(),
imported: now.clone(),
updated: now.clone(),
password_changed: now.clone(),
last_used: now,
access_count: 0,
};
let dr = DecryptedRecord { creds, metadata };
app.db.insert(dr);
app.db.close()?;
Ok(())
}
70 changes: 66 additions & 4 deletions src/cli/command/arg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use clap::Arg;

// Database Flags

pub fn db_arg() -> Arg {
Arg::new("db")
.help("path to the encrypted database to use")
Expand All @@ -9,16 +11,76 @@ pub fn db_arg() -> Arg {
}

pub fn pwd_arg() -> Arg {
Arg::new("password")
Arg::new("db-pass")
.help("password used to encrypt the database")
.short('p')
.long("password")
.long("db-pass")
}

pub fn salt_arg() -> Arg {
Arg::new("salt")
.help("the salt to use for encrypting the database")
.default_value(env!("USER"))
.default_value(default_salt())
.short('s')
.long("salt")
}

fn default_salt() -> String {
match std::env::var("USER") {
Ok(user) => user,
Err(_) => "rucksack".to_string(),
}
}

// Account Flags

pub fn account_type() -> Arg {
Arg::new("type")
.help("the type of secret to add")
.short('t')
.long("type")
// These next have not yet been defined/refined:
.value_parser(["", "account", "credential", "creds", "password"])
}

pub fn account_user() -> Arg {
Arg::new("user")
.help("the user, login, or account, identifier")
.short('u')
.long("user")
}

pub fn account_user_old() -> Arg {
Arg::new("old-user")
.help("the old user login name")
.short('u')
.long("old-user")
}

pub fn account_user_new() -> Arg {
Arg::new("new-user")
.help("the new user login name to use")
.short('u')
.long("new-user")
}

pub fn account_pass() -> Arg {
Arg::new("password")
.help("the account / login password")
.long("password")
}

pub fn account_url() -> Arg {
Arg::new("url").help("the login URL").long("url")
}

pub fn account_url_old() -> Arg {
Arg::new("old-url")
.help("the old login URL")
.long("old-url")
}

pub fn account_url_new() -> Arg {
Arg::new("new-url")
.help("the new URL for the account / login")
.long("new-url")
}
Loading