Skip to content

Commit

Permalink
remove separate cache cli option, merge it into sync. use vars for lo…
Browse files Browse the repository at this point in the history
…gin and unlock (#79)
  • Loading branch information
blacs30 committed Jun 5, 2021
1 parent 2302290 commit eef1268
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 134 deletions.
24 changes: 12 additions & 12 deletions README.md
Expand Up @@ -65,7 +65,6 @@ You can change the search-/filtermode yourself easily. This gif shows the 3 step
| bwf_keyword | defines the keyword which opens the folder search of the Bitwarden Alfred Workflow | .bwf |
| bwauth_keyword | defines the keyword which opens the Bitwarden authentications of the Alfred Workflow | .bwauth |
| bwconf_keyword | defines the keyword which opens the Bitwarden configuration/settings of the Alfred Workflow | .bwconfig |
| CACHE_AGE | This defines how old the cached items can get, if expired the Workflow will trigger a new cache refresh with Bitwarden (this does not involve a sync with the Bitwarden server, values lower than 30 will be ignored and set to 30) | 0 (0 means disabled; unit is minutes) |
| DEBUG | If enabled print additional debug information, specially about for the decryption process | false |
| EMAIL | the email which to use for the login via the Bitwarden CLI, will be read from the data.json of the Bitwarden CLI if present | "" |
| EMPTY_DETAIL_RESULTS | Show all information in the detail view, also if the content is empty | false |
Expand All @@ -85,8 +84,9 @@ You can change the search-/filtermode yourself easily. This gif shows the 3 step
| PATH | The PATH env variable which is used to search for executables (like the Bitwarden CLI configured with BW_EXEC, security to get and set keychain objects) | /usr/bin:/usr/local/bin:/usr/local/sbin:/usr/local/share/npm/bin:/usr/bin:/usr/sbin |
| REORDERING_DISABLED | If set to false the items which are often selected appear further up in the results. | true |
| SERVER_URL | Set the server url if you host your own Bitwarden instance - you can also set separate domains for api,webvault etc e.g. `--api http://localhost:4000 --identity http://localhost:33656` | https://bitwarden.com |
| SYNC_CACHE_AGE | This defines how old the sync cache can get, if expired the Workflow will trigger a new sync with Bitwarden (values lower than 30 will be ignored and set to 30) | 0 (0 means disabled; unit is minutes) |
| SYNC_CACHE_AGE | This defines how old the sync cache can get, if expired the Workflow will trigger a new sync with Bitwarden (values lower than 30 will be ignored and set to 30) | 10080 (0 means disabled; unit is minutes) |
| TITLE_WITH_USER | If enabled the name of the login user item or the last 4 numbers of the card number will be appended (added) at the end of the name of the item | true |
| TITLE_WITH_URLS | If enabled all the URLs for an login item will be appended (added) at the end of the name of the item | true |

## Modifier Actions Explained

Expand All @@ -102,19 +102,19 @@ You can change the search-/filtermode yourself easily. This gif shows the 3 step
| identity | - (always copy the name ) |
| others | more (to show all item entries, can't be NO_MODIFIER_ACTION) |

You can place per type one `action name` into the ACTION config, a combination is possible where it is not overlapping with `more` or another of the same type.
You can place per type *one* `action name` into the ACTION config, a combination is possible where it is *not* overlapping with `more` or another of the same type.

Good examples:
**Good examples:**

NO_MODIFIER_ACTION=url,code
MODIFIER_1_ACTION=totp
MODIFIER_2_ACTION=more
MODIFIER_3_ACTION=password,card
NO_MODIFIER_ACTION=url,code<br>
MODIFIER_1_ACTION=totp<br>
MODIFIER_2_ACTION=more<br>
MODIFIER_3_ACTION=password,card (2 items listed but of different *type*)

Bad examples:
**Bad examples:**

NO_MODIFIER_ACTION=url,password
MODIFIER_3_ACTION=code,card
NO_MODIFIER_ACTION=url,password<br>
MODIFIER_3_ACTION=code,card (2 items listed but of the same *type*, therefore this is not permitted and will cause problems)

# Develop locally

Expand Down Expand Up @@ -195,7 +195,7 @@ A big thanks to all code contributors but also to everyone who creates issues an

To use the workflows faster decryption you can [follow this instruction by Bitwarden](https://bitwarden.com/help/article/update-encryption-key/)) <br>
to update the encryption keys to the new mechanism.

The linked doc doesn't specify how to force creation of a new key. It's easy though:

- Login to your vault.
Expand Down
19 changes: 2 additions & 17 deletions bitwarden.go
Expand Up @@ -45,7 +45,7 @@ func runSync(force bool, last bool) {
}

if opts.Background {
log.Println("Runnung sync in background")
log.Println("Running sync in background")
if !wf.IsRunning("sync") {
log.Printf("Starting sync job.")
cmd := exec.Command(os.Args[0], "-sync", "-force")
Expand Down Expand Up @@ -99,7 +99,7 @@ func runSync(force bool, last bool) {
getItems()

// Writing the sync-cache
err = wf.Cache.Store(SYNC_CACHE_NAME, []byte(string("sync-cache")))
err = wf.Cache.Store(SYNC_CACHE_NAME, []byte("sync-cache"))
if err != nil {
log.Println(err)
}
Expand Down Expand Up @@ -576,21 +576,6 @@ func runCache() {
return
}

log.Println("Background?", opts.Background)
if opts.Background {
if !wf.IsRunning("cache") {
cmd := exec.Command(os.Args[0], "-cache")
log.Println("Cache cmd: ", cmd)
if err := wf.RunInBackground("cache", cmd); err != nil {
wf.FatalError(err)
}
} else {
log.Printf("Cache job already running.")
}
searchAlfred(conf.BwKeyword)
return

}
log.Println("Running cache")
getItems()
}
80 changes: 15 additions & 65 deletions cli.go
Expand Up @@ -14,7 +14,6 @@ import (
"os"
"os/exec"
"strconv"
"sync"
)

var (
Expand Down Expand Up @@ -44,7 +43,6 @@ type options struct {
Force bool
Totp bool
Last bool
Cache bool
Background bool

// Arguments
Expand All @@ -71,7 +69,6 @@ func init() {
cli.BoolVar(&opts.Logout, "logout", false, "logout Bitwarden")
cli.BoolVar(&opts.Sync, "sync", false, "sync secrets")
cli.BoolVar(&opts.Background, "background", false, "Run job in background")
cli.BoolVar(&opts.Cache, "cache", false, "refresh workflow cache")
cli.BoolVar(&opts.Last, "last", false, "last sync")
cli.BoolVar(&opts.Force, "force", false, "force full sync")
cli.BoolVar(&opts.Totp, "totp", false, "get totp for item id")
Expand All @@ -85,7 +82,6 @@ Alfred workflow to get secrets from Bitwarden.
Usage:
bitwarden-alfred-workflow [<query>]
bitwarden-alfred-workflow -auth [<query>]
bitwarden-alfred-workflow -cache [-background]
bitwarden-alfred-workflow -conf [<query>]
bitwarden-alfred-workflow -folder [<query>]
bitwarden-alfred-workflow -getitem -id <id> [-totp] [-attachment <id>] [<query>] (query is used as jsonpath)
Expand Down Expand Up @@ -261,8 +257,6 @@ func runConfig() {
Var("notification", "Downloading Favicons for URLs").
Arg("-background")

addRefreshCacheItem()

wf.NewItem("Get date of last Bitwarden secret sync").
Subtitle("Show the date when the last sync happened with the Bitwarden server.").
Valid(true).
Expand Down Expand Up @@ -315,7 +309,10 @@ func runAuth() {
UID("login").
Icon(iconOn).
Var("action", "-login").
Arg("login", email, fmt.Sprintf("%d", sfaMode), map2faMode(sfaMode))
Var("type", "login").
Var("email", email).
Var("sfamode", fmt.Sprintf("%d", sfaMode)).
Var("mapsfamode", map2faMode(sfaMode))

wf.NewItem("Logout").
Subtitle("Logout from Bitwarden").
Expand All @@ -330,7 +327,8 @@ func runAuth() {
Valid(true).
Icon(iconOn).
Var("action", "-unlock").
Arg("unlock", email)
Var("type", "unlock").
Var("email", email)

wf.NewItem("Lock").
Subtitle("Lock Bitwarden").
Expand Down Expand Up @@ -502,13 +500,13 @@ func runSearch(folderSearch bool, itemId string) {
if bwData.UserId == "" {
message := "Need to login first."
if wf.Cache.Exists(CACHE_NAME) && wf.Cache.Exists(FOLDER_CACHE_NAME) {
message = "Need to login first to get secrets, reading cached no secret items only."
message = "Need to login first to get secrets, reading cached items without the secret."
}
addLoginWarningItem(wf, email, sfaMode, message)
}

if bwData.UserId != "" && bwData.ProtectedKey == "" {
message := "Need to unlock first to get secrets, reading cached no secret items only."
message := "Need to unlock first to get secrets, reading cached items without the secrets."
addUnlockWarningItem(wf, email, message)
}

Expand Down Expand Up @@ -544,7 +542,11 @@ func runSearch(folderSearch bool, itemId string) {
// Check the sync cache, if it expired.
// don't sync if age is set to 0
// this cache is just a control to automatically trigger the sync, the data itself is stored in the data cache (CACHE_NAME and FOLDER_CACHE_NAME)
if conf.SyncCacheAge != 0 && wf.Cache.Expired(SYNC_CACHE_NAME, conf.SyncMaxCacheAge) {

// If the cache has expired, set Rerun (which tells Alfred to re-run the
// workflow), and start the background update process if it isn't already
// running.
if conf.SyncCacheAge != 0 && (wf.Cache.Expired(SYNC_CACHE_NAME, conf.SyncMaxCacheAge) || (wf.Cache.Expired(CACHE_NAME, conf.SyncMaxCacheAge) || wf.Cache.Expired(FOLDER_CACHE_NAME, conf.SyncMaxCacheAge))) {
if !wf.IsRunning("sync") {
cmd := exec.Command(os.Args[0], "-sync", "-force")
log.Println("Sync cmd: ", cmd)
Expand All @@ -562,33 +564,6 @@ func runSearch(folderSearch bool, itemId string) {
return
}

// If the cache has expired, set Rerun (which tells Alfred to re-run the
// workflow), and start the background update process if it isn't already
// running.
if conf.CacheAge != 0 && (wf.Cache.Expired(CACHE_NAME, conf.MaxCacheAge) || wf.Cache.Expired(FOLDER_CACHE_NAME, conf.MaxCacheAge)) {
wf.Rerun(0.3)
if !wf.IsRunning("cache") {
var wg sync.WaitGroup
wg.Add(1)
run := checkBitwardenAuthInWorkflow(wf, &wg, email, sfaMode)
wg.Wait()

if run {
// now we can run the background job
cmd := exec.Command(os.Args[0], "-cache")
if err := wf.RunInBackground("cache", cmd); err != nil {
wf.FatalError(err)
}
}
} else {
log.Printf("Cache job already running.")
}
wf.NewItem("Refreshing Bitwarden cache…").
Icon(ReloadIcon())
wf.SendFeedback()
return
}

// If iconcache enabled and the cache is expired (or doesn't exist)
if conf.IconCacheEnabled && (wf.Data.Expired(ICON_CACHE_NAME, conf.IconMaxCacheAge) || !wf.Data.Exists(ICON_CACHE_NAME)) {
getIcon(wf)
Expand Down Expand Up @@ -650,8 +625,7 @@ func runSearch(folderSearch bool, itemId string) {
}

if len(items) == 0 && len(folders) == 0 {
addRefreshCacheItem()
wf.NewItem("No Secrets Found").Subtitle("Try a different query or refresh the cache or sync manually.").Icon(iconWarning).Valid(false)
wf.NewItem("No Secrets Found").Subtitle("Try a different query or sync manually.").Icon(iconWarning).Valid(false)
}

if !folderSearch && itemId == "" {
Expand Down Expand Up @@ -701,29 +675,6 @@ func addUnlockWarningItem(wf *aw.Workflow, email string, message string) {
Arg("unlock", email)
}

func checkBitwardenAuthInWorkflow(wf *aw.Workflow, wg *sync.WaitGroup, email string, sfaMode int) bool {
// check if logged in or locked first
loginErr, unlockErr := BitwardenAuthChecks()
if loginErr != nil {
wf.Rerun(0)
message := "Need to login first."
addLoginWarningItem(wf, email, sfaMode, message)
wg.Done()
wf.SendFeedback()
return false
}
if unlockErr != nil {
wf.Rerun(0)
message := "Need to unlock first."
addUnlockWarningItem(wf, email, message)
wg.Done()
wf.SendFeedback()
return false
}
wg.Done()
return true
}

// Filter Bitwarden secrets in Alfred
func runSearchFolder(items []Item, folders []Folder) {
if opts.Query != "" {
Expand Down Expand Up @@ -768,8 +719,7 @@ func runSearchFolder(items []Item, folders []Folder) {
}

if len(items) == 0 && len(folders) == 0 {
addRefreshCacheItem()
wf.WarnEmpty("No Secrets Found", "Try a different query or refresh the cache manually.")
wf.WarnEmpty("No Secrets Found", "Try a different query or sync manually.")
}
wf.WarnEmpty("No Folders Found", "Try a different query.")
wf.SendFeedback()
Expand Down
32 changes: 21 additions & 11 deletions config.go
Expand Up @@ -84,15 +84,13 @@ type config struct {
BwExec string `split_words:"true"`
// BwDataPath default is set in loadBitwardenJSON()
BwDataPath string `envconfig:"BW_DATA_PATH"`
CacheAge int `default:"1440" split_words:"true"`
Debug bool `envconfig:"DEBUG" default:"false"`
Email string
EmptyDetailResults bool `default:"false" split_words:"true"`
IconCacheAge int `default:"43200" split_words:"true"`
IconCacheEnabled bool `default:"true" split_words:"true"`
IconMaxCacheAge time.Duration
MaxResults int `default:"1000" split_words:"true"`
MaxCacheAge time.Duration
MaxResults int `default:"1000" split_words:"true"`
Mod1 string `envconfig:"MODIFIER_1" default:"alt"`
Mod1Action string `envconfig:"MODIFIER_1_ACTION" default:"username,code"`
Mod2 string `envconfig:"MODIFIER_2" default:"shift"`
Expand All @@ -110,7 +108,8 @@ type config struct {
SfaMode int `envconfig:"2FA_MODE" default:"0"`
SyncCacheAge int `default:"1440" split_words:"true"`
SyncMaxCacheAge time.Duration
TitleWithUser bool `envconfig:"TITLE_WITH_USER" default:"false"`
TitleWithUser bool `envconfig:"TITLE_WITH_USER" default:"true"`
TitleWithUrls bool `envconfig:"TITLE_WITH_URLS" default:"true"`
}

type BwData struct {
Expand Down Expand Up @@ -179,13 +178,6 @@ func loadConfig() {
conf.OutputFolder = alfred.GetOutputFolder(wf, conf.OutputFolder)

// Set a few cache timeout durations
setItemCacheAge := conf.CacheAge
if conf.CacheAge < 30 && conf.CacheAge != 0 {
setItemCacheAge = 30
}
cacheAgeDuration := time.Duration(setItemCacheAge)
conf.MaxCacheAge = cacheAgeDuration * time.Minute

iconCacheAgeDuration := time.Duration(conf.IconCacheAge)
conf.IconMaxCacheAge = iconCacheAgeDuration * time.Minute

Expand Down Expand Up @@ -325,6 +317,15 @@ func setModAction(itemConfig *itemsModifierActionRelation, item Item, itemType s
if conf.TitleWithUser {
title = fmt.Sprintf("%s - %s", item.Name, item.Login.Username)
}

var urlList string
for _, url := range item.Login.Uris {
urlList = fmt.Sprintf("%s - %s", urlList, url.Uri)
}
if conf.TitleWithUrls {
title = fmt.Sprintf("%s - %s", title, urlList)
}

if action == "password" {
subtitle := "Copy password"
if modMode == "nomod" {
Expand Down Expand Up @@ -429,6 +430,15 @@ func setModAction(itemConfig *itemsModifierActionRelation, item Item, itemType s
if conf.TitleWithUser {
title = fmt.Sprintf("%s - %s", item.Name, item.Card.Number)
}

var urlList string
for _, url := range item.Login.Uris {
urlList = fmt.Sprintf("%s - %s", urlList, url.Uri)
}
if conf.TitleWithUrls {
title = fmt.Sprintf("%s - %s", title, urlList)
}

if action == "card" {
subtitle := "Copy Card Number"
if modMode == "nomod" {
Expand Down
11 changes: 0 additions & 11 deletions items.go
Expand Up @@ -525,14 +525,3 @@ func addNewModifierItem(item *aw.Item, modifier modifierActionRelation) {
Arg(modifier.Content.Arg).
Icon(modifier.Content.Icon)
}

func addRefreshCacheItem() {
wf.NewItem("Refresh Bitwardens Secret Cache").
Subtitle("Fill the cache with cleaned Bitwarden secrets (the real secrets are not kept in the cached)").
Valid(true).
UID("cache").
Icon(ReloadIcon()).
Var("action", "-cache").
Var("notification", "Refreshing Bitwarden Workflow cache").
Arg("-background")
}
14 changes: 1 addition & 13 deletions main.go
Expand Up @@ -122,13 +122,6 @@ func checkIfJobRuns() {
wf.SendFeedback()
return
}
if wf.IsRunning("cache") {
wf.Rerun(0.3)
wf.NewItem("Refreshing Bitwarden cache…").
Icon(ReloadIcon())
wf.SendFeedback()
return
}
if wf.IsRunning("icons") {
wf.Rerun(0.3)
wf.NewItem("Refreshing Icon cache…").
Expand Down Expand Up @@ -183,7 +176,7 @@ func run() {

checkIfJobRuns()

if !wf.IsRunning("cache") && !wf.IsRunning("sync") && !wf.IsRunning("icons") {
if !wf.IsRunning("sync") && !wf.IsRunning("icons") {
pidfilePath := fmt.Sprintf("/tmp/%s", WORKFLOW_NAME)
processName := WORKFLOW_NAME
pidHandler(pidfilePath)
Expand Down Expand Up @@ -271,11 +264,6 @@ func run() {
return
}

if opts.Cache {
runCache()
return
}

if opts.GetItem {
runGetItem()
return
Expand Down

0 comments on commit eef1268

Please sign in to comment.