Skip to content

Commit

Permalink
Gnome Search Provider for Ripasso
Browse files Browse the repository at this point in the history
Implements a GNOME search provider for Ripasso. This is a very
convenient way to obtain passwords from pass when using the GNOME shell.

I totally understand if this isn't the kind of addition you would like
to see in this repo, especially if you don't use GNOME. So I have no
problem spinning it off into it's own project. It's only about 150 LoC.
But since there is already a GTK project in here, I figured why not give
it a shot.

There will need to be some additions made for the configuration files. I
know there is at least an arch linux package, and I'd be happy to
contribute to that.

Thanks for your work on Ripasso!
  • Loading branch information
Fingel committed Sep 18, 2024
1 parent 998e87f commit 8810b45
Show file tree
Hide file tree
Showing 10 changed files with 692 additions and 5 deletions.
478 changes: 474 additions & 4 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ criterion = "0.5.1"

members = [
"gtk", "cursive"
]
, "gnome_search_provider"]

[[bench]]
name = "library_benchmark"
Expand Down
13 changes: 13 additions & 0 deletions gnome_search_provider/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ripasso-gnome-search-provider"
version = "0.1.0"
edition = "2021"
authors = ["Austin Riba <[email protected]>"]

[dependencies]
arboard = { version = "3.4.1", features = ["wayland-data-control"] }
ripasso = { path = "../", version = "0.7.0-alpha" }
search-provider = "0.10.0"
tokio = { version = "1.40.0", features = ["macros"] }
zbus = { version = "4.4.0", features = ["tokio"] }
zeroize = "1.8.1"
13 changes: 13 additions & 0 deletions gnome_search_provider/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Ripasso Gnome Search Provider

WIP search provider for GNOME using ripasso.

## Setting a custom PASSWORD_STORE_DIR

Use `systemctl --user edit org.gnome.Ripasso.SearchProvider.service`
to create an override file that contains the following:

```ini
[Service]
Environment="PASSWORD_STORE_DIR=/path/to/password-store"
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Categories=GNOME;Security;
Icon=dialog-password
Name=Ripasso
Comment=GNOME Shell search provider for Ripasso
Terminal=true
Type=Application
OnlyShowIn=GNOME;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[D-BUS Service]
Name=org.gnome.Ripasso.SearchProvider
Exec=/usr/lib/ripasso-search-provider/ripasso-gnome-search-provider
SystemdService=org.gnome.Ripasso.SearchProvider.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Unit]
Description=Ripasso Gnome Shell search provider for GNOME

[Service]
Type=dbus
BusName=org.gnome.Ripasso.SearchProvider
ExecStart=/usr/lib/ripasso-search-provider/ripasso-gnome-search-provider
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[Shell Search Provider]
DesktopId=org.gnome.Ripasso.SearchProvider.desktop
BusName=org.gnome.Ripasso.SearchProvider
ObjectPath=/org/gnome/Ripasso/SearchProvider
Version=2
16 changes: 16 additions & 0 deletions gnome_search_provider/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -eu -o pipefail
cd "$(dirname "$(realpath "${0}")")"

DATADIR=${DATADIR:-/usr/share}
LIBDIR=${LIBDIR:-/usr/lib}

install -Dm 0755 ../target/release/ripasso-gnome-search-provider "${LIBDIR}"/ripasso-search-provider/ripasso-gnome-search-provider

install -Dm 0644 conf/org.gnome.Ripasso.search-provider.ini "${DATADIR}"/gnome-shell/search-providers/org.gnome.Ripasso.search-provider.ini

install -Dm 0644 conf/org.gnome.Ripasso.SearchProvider.desktop "${DATADIR}"/applications/org.gnome.Ripasso.SearchProvider.desktop

install -Dm 0644 conf/org.gnome.Ripasso.SearchProvider.service.dbus "${DATADIR}"/dbus-1/services/org.gnome.Ripasso.SearchProvider.service

install -Dm 0644 conf/org.gnome.Ripasso.SearchProvider.service.systemd "${LIBDIR}"/systemd/user/org.gnome.Ripasso.SearchProvider.service
150 changes: 150 additions & 0 deletions gnome_search_provider/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use arboard::Clipboard;
use ripasso::pass::PasswordStore;
use search_provider::{ResultID, ResultMeta, SearchProvider, SearchProviderImpl};
use std::{collections::HashMap, path::PathBuf, thread, time};
use zbus::{blocking::Connection, proxy, zvariant::Value};
use zeroize::Zeroize;

#[proxy(
default_service = "org.freedesktop.Notifications",
default_path = "/org/freedesktop/Notifications"
)]
trait Notifications {
fn notify(
&self,
app_name: &str,
replaces_id: u32,
app_icon: &str,
summary: &str,
body: &str,
actions: &[&str],
hints: HashMap<&str, &Value<'_>>,
expire_timeout: i32,
) -> zbus::Result<u32>;
}

fn copy_to_clipbard(content: &String) {
let mut clipboard = Clipboard::new().unwrap();
clipboard.set_text(content).unwrap();
thread::spawn(|| {
thread::sleep(time::Duration::from_secs(40));
let mut clipboard = Clipboard::new().unwrap();
clipboard.set_text(&String::new()).unwrap();
});
}

fn send_notification(summary: String, body: String) {
thread::spawn(move || {
let connection = Connection::session().unwrap();
let proxy = NotificationsProxyBlocking::new(&connection).unwrap();
let _ = proxy.notify(
"ripasso",
0,
"dialog-password",
&summary,
&body,
&[],
HashMap::from([("transient", &Value::Bool(true))]),
4000,
);
})
.join()
.unwrap();
}

struct Application {
password_store: PasswordStore,
}

impl SearchProviderImpl for Application {
fn activate_result(&self, identifier: ResultID, terms: &[String], _timestamp: u32) {
let passwords = self.password_store.all_passwords().unwrap_or_default();
if let Some(password) = passwords
.iter()
.find(|entry| entry.name == identifier.to_owned())
{
if terms[0] == "otp" {
let mut otp = match password.mfa(&self.password_store) {
Ok(otp) => otp,
Err(err) => {
send_notification("OTP Error".to_string(), err.to_string());
return;
}
};
copy_to_clipbard(&otp);
otp.zeroize();
send_notification(identifier, "OTP copied to clipboard".to_string());
} else {
let mut secret = match password.password(&self.password_store) {
Ok(secret) => secret,
Err(err) => {
send_notification("Password Error".to_string(), err.to_string());
return;
}
};
copy_to_clipbard(&secret);
secret.zeroize();
send_notification(identifier, "Password copied to clipboard".to_string());
}
} else {
send_notification("Error".to_string(), "Could Not Find Password".to_string());
}
}

fn initial_result_set(&self, terms: &[String]) -> Vec<ResultID> {
let search_terms = if terms[0] == "otp" {
&terms[1..]
} else {
terms
};

self.password_store
.all_passwords()
.unwrap_or_default()
.iter()
.filter(|entry| {
search_terms
.iter()
.any(|term| entry.name.to_lowercase().contains(&term.to_lowercase()))
})
.map(|entry| entry.name.to_owned())
.collect()
}

fn result_metas(&self, identifiers: &[ResultID]) -> Vec<ResultMeta> {
identifiers
.iter()
.map(|id| ResultMeta::builder(id.to_owned(), id).build())
.collect()
}
}

#[tokio::main]
async fn main() -> zbus::Result<()> {
let home = std::env::var("HOME").expect("Could not determine $HOME");
let home_path = PathBuf::from(home);
let default_path = match std::env::var("PASSWORD_STORE_DIR") {
Ok(val) => PathBuf::from(val),
Err(_) => [home_path.to_str().unwrap(), ".password-store"]
.iter()
.collect(),
};
let password_store = PasswordStore::new(
"default",
&Some(default_path),
&None,
&Some(home_path),
&None,
&ripasso::crypto::CryptoImpl::GpgMe,
&None,
)
.unwrap();
let app = Application { password_store };
SearchProvider::new(
app,
"org.gnome.Ripasso.SearchProvider",
"/org/gnome/Ripasso/SearchProvider",
)
.await?;
Ok(())
}

0 comments on commit 8810b45

Please sign in to comment.