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

Feature: Local cache for op items #16

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
60 changes: 58 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This plugin relies on the following:
In any tmux mode:

- `prefix + u` - list login items in a bottom pane.
- `prefix + C` - clear the cache of tmux-1password.

## Install

Expand Down Expand Up @@ -77,8 +78,9 @@ sessions expires automatically after 30 minutes of inactivity.
### Configuring login items in 1Password

In order to show only relevant login items and to maintain compatibility with
[sudolikeaboss](https://github.com/ravenac95/sudolikeaboss), its required to set the value of the
`website` field for each login item with the value of `sudolikeaboss://local`.
[sudolikeaboss](https://github.com/ravenac95/sudolikeaboss), by default it is required to set the value of the
`website` field for each login item with the value of `sudolikeaboss://local`. To override this behavior and provide
customized filtering, see configuration options `@1password-enabled-url-filter` and `@1password-items-jq-filter` below.

delucca marked this conversation as resolved.
Show resolved Hide resolved
## Configuration

Expand Down Expand Up @@ -121,6 +123,60 @@ set -g @1password-copy-to-clipboard 'on'

Default: `'off'`

#### Enabled URL Filter

By default, the plugin maintains compatibility with [sudolikeaboss](https://github.com/ravenac95/sudolikeaboss) by
filtering urls using the string "sudolikeaboss://local", by setting the following, the list of items will no longer be
filtered.

```
set -g @1password-enabled-url-filter 'off'
```

Default: `'on'`

#### Customize URL Filtering

If complete customization of url filtering is required, a `jq` filter can be provided to filter and map
items.

##### Filtering by tags

```
set -g @1password-items-jq-filter '.[] | [select(.overview.tags | map(select(. == "tag_name")) | length == 1)?] | map([ .overview.title, .uuid ] | join(",")) | .[]'
```

##### Filtering by custom url

```
set -g @1password-items-jq-filter '.[] | [select(.overview.URLs | map(select(.u == "myspecial-url-filter")) | length == 1)?] | map([ .overview.title, .uuid ] | join(",")) | .[]'
```

Default: `''`

Items come in the following format from which the filter operates:

```
[
{
"uuid": "some-long-uuid",
"overview": {
"URLs": [
{ "u": "sudolikeaboss://local" }
],
"title": "Some item",
"tags": ["tag_name"]
}
}
]
```

## Security

This plugin is based on using `op list-items` to get a filtered list of passwords from your vault, and them asking for the password you want with `op get-item`. To improve the performance, we've added a cache file which has a TTL of 30 minutes and stores a simple list containing your account names and the related IDs.

**No password is stored on the disk,** just a simple pointer to be used in the future when you ask to fetch a specific password.

## Prior art

Also see:
Expand Down
3 changes: 3 additions & 0 deletions plugin.tmux
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ main() {
done

local -r opt_key="$(get_tmux_option "@1password-key" "u")"
local -r clear_key="$(get_tmux_option "@1password-key" "C")"
delucca marked this conversation as resolved.
Show resolved Hide resolved

tmux bind-key "$opt_key" \
run "tmux split-window -l 10 \"$CURRENT_DIR/scripts/main.sh '#{pane_id}'\""

tmux bind-key "$clear_key" run "$CURRENT_DIR/scripts/main.sh clear-cache"
}

main "$@"
80 changes: 71 additions & 9 deletions scripts/main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ source "./spinner.sh"

# ------------------------------------------------------------------------------

declare -r TMP_TOKEN_FILE="$HOME/.op_tmux_token_tmp"
declare -r TMP_TOKEN_FILE="/tmp/tmux-op-token"
declare -r CACHE_FILE="/tmp/tmux-op-items"
declare -r CACHE_TTL=1800 # 30 minutes, since we cannot fetch passwords with invalid session token

declare -r OPT_SUBDOMAIN="$(get_tmux_option "@1password-subdomain" "my")"
declare -r OPT_VAULT="$(get_tmux_option "@1password-vault" "")"
declare -r OPT_COPY_TO_CLIPBOARD="$(get_tmux_option "@1password-copy-to-clipboard" "off")"
declare -r OPT_ENABLED_URL_FILTER="$(get_tmux_option "@1password-enabled-url-filter" "on")"
declare -r OPT_ITEMS_JQ_FILTER="$(get_tmux_option "@1password-items-jq-filter" "")"

declare spinner_pid=""

Expand Down Expand Up @@ -44,7 +48,29 @@ op_get_session() {
}

get_op_items() {
clear_old_cache

if [[ -e $CACHE_FILE ]]; then
echo "$(cat $CACHE_FILE)"
else
fetch_items
fi
}

clear_old_cache() {
if [[ -e $CACHE_FILE ]]; then
local last_update="$(stat -c %Y $CACHE_FILE)"
local now="$(date +%s)"
local seconds_since_last_update="$(($now-$last_update))"

# Remove cache file if last cache was from 30 minutes ago
if [[ $seconds_since_last_update < $CACHE_TTL ]]; then
rm $CACHE_FILE
fi
fi
}

fetch_items() {
# The structure (we need) looks like the following:
# [
# {
Expand All @@ -58,18 +84,38 @@ get_op_items() {
# }
# ]

local -r JQ_FILTER="
.[]
| [select(.overview.URLs | map(select(.u == \"sudolikeaboss://local\")) | length == 1)?]
| map([ .overview.title, .uuid ]
| join(\",\"))
| .[]
"
if [ -n "$OPT_ITEMS_JQ_FILTER" ]; then
local -r JQ_FILTER="$OPT_ITEMS_JQ_FILTER"
elif [ "$OPT_ENABLED_URL_FILTER" == "on" ]; then
local -r JQ_FILTER="
.[]
| [select(.overview.URLs | map(select(.u == \"sudolikeaboss://local\")) | length == 1)?]
| map([ .overview.title, .uuid ]
| join(\",\"))
| .[]
"
else
local -r JQ_FILTER="
.[]
| [select(.overview.URLs | map(select(.u)) | length == 1)?]
| map([ .overview.title, .uuid ]
| join(\",\"))
| .[]
"
fi

op list items --vault="$OPT_VAULT" --session="$(op_get_session)" 2> /dev/null \
| jq "$JQ_FILTER" --raw-output
}

cache_items() {
local items=$1

if ! [[ -e $CACHE_FILE ]]; then
echo "$items" > $CACHE_FILE
fi
}

get_op_item_password() {
local -r ITEM_UUID="$1"

Expand Down Expand Up @@ -113,6 +159,22 @@ get_op_item_password() {
# ------------------------------------------------------------------------------

main() {
delucca marked this conversation as resolved.
Show resolved Hide resolved
local -r command=$@

if [[ $command == "clear-cache" ]]; then
clear_cache
else
prompt_op $@
fi
}

clear_cache() {
rm $CACHE_FILE

display_message "Cache cleared"
}

prompt_op() {
local -r ACTIVE_PANE="$1"

local items
Expand All @@ -139,6 +201,7 @@ main() {
spinner_stop
fi

cache_items "$items"
selected_item_name="$(echo "$items" | awk -F ',' '{ print $1 }' | fzf --no-multi)"

if [[ -n "$selected_item_name" ]]; then
Expand All @@ -156,7 +219,6 @@ main() {
# Clear clipboard
clear_clipboard 30
else

# Use `send-keys`
tmux send-keys -t "$ACTIVE_PANE" "$selected_item_password"
fi
Expand Down