diff --git a/action.list.yaml b/action.list.yaml new file mode 100644 index 0000000..fb0f177 --- /dev/null +++ b/action.list.yaml @@ -0,0 +1,5 @@ +runtime: plugin +action: + title: "Keyring: List" + description: >- + Returns list of all stored key-value pairs and URLs. \ No newline at end of file diff --git a/keyring.go b/keyring.go index f6fb98a..96f9532 100644 --- a/keyring.go +++ b/keyring.go @@ -45,6 +45,10 @@ func (i KeyValueItem) isEmpty() bool { // DataStore provides password storage functionality. type DataStore interface { + // GetUrls retrieves a list of stored URLs. + GetUrls() ([]string, error) + // GetKeys retrieves a list of stored keys. + GetKeys() ([]string, error) // GetForURL returns a credentials item by a URL. // Error is returned if either the keyring could not be unlocked // Error ErrNotFound if the credentials were not found. @@ -124,6 +128,26 @@ func (k *keyringService) defaultStore() (DataStore, error) { return k.store, nil } +// GetUrls implements DataStore interface. Uses service default store. +func (k *keyringService) GetUrls() ([]string, error) { + s, err := k.defaultStore() + if err != nil { + return []string{}, err + } + + return s.GetUrls() +} + +// GetKeys implements DataStore interface. Uses service default store. +func (k *keyringService) GetKeys() ([]string, error) { + s, err := k.defaultStore() + if err != nil { + return []string{}, err + } + + return s.GetKeys() +} + // GetForURL implements DataStore interface. Uses service default store. func (k *keyringService) GetForURL(url string) (CredentialsItem, error) { s, err := k.defaultStore() diff --git a/plugin.go b/plugin.go index da475d9..cfa27d5 100644 --- a/plugin.go +++ b/plugin.go @@ -32,6 +32,8 @@ var ( ) var ( + //go:embed action.list.yaml + actionListYaml []byte //go:embed action.login.yaml actionLoginYaml []byte //go:embed action.logout.yaml @@ -107,6 +109,17 @@ func processGetByKey(value any, opts GetKeyValueProcessorOptions, ctx action.Val // DiscoverActions implements [launchr.ActionDiscoveryPlugin] interface. func (p *Plugin) DiscoverActions(_ context.Context) ([]*action.Action, error) { + // Action list. + listCmd := action.NewFromYAML("keyring:list", actionListYaml) + listCmd.SetRuntime(action.NewFnRuntime(func(_ context.Context, a *action.Action) error { + printer := launchr.Term() + if rt, ok := a.Runtime().(action.RuntimeTermAware); ok { + printer = rt.Term() + } + + return list(p.k, printer) + })) + // Action login. loginCmd := action.NewFromYAML("keyring:login", actionLoginYaml) loginCmd.SetRuntime(action.NewFnRuntime(func(_ context.Context, a *action.Action) error { @@ -161,6 +174,7 @@ func (p *Plugin) DiscoverActions(_ context.Context) ([]*action.Action, error) { })) return []*action.Action{ + listCmd, loginCmd, logoutCmd, setKeyCmd, @@ -198,6 +212,39 @@ func buildNotFoundError(item, template string, err error) error { return fmt.Errorf(template, item, version.Name) } +func list(k Keyring, printer *launchr.Terminal) error { + urls, err := k.GetUrls() + if err != nil { + return err + } + + keys, err := k.GetKeys() + if err != nil { + return err + } + + // Show both key-value pairs and URLs + if len(keys) > 0 { + printer.Info().Printfln("Key-value pairs:") + for _, key := range keys { + printer.Printfln("- %s", key) + } + } + + if len(urls) > 0 { + printer.Info().Printfln("URLs:") + for _, url := range urls { + printer.Printfln("- %s", url) + } + } + + if len(urls) == 0 && len(keys) == 0 { + printer.Info().Printfln("No items found in keyring") + } + + return nil +} + func login(k Keyring, creds CredentialsItem) error { // Ask for login elements if some elements are empty. if creds.isEmpty() { diff --git a/yaml.go b/yaml.go index 2cd830f..94bb458 100644 --- a/yaml.go +++ b/yaml.go @@ -58,6 +58,34 @@ func (s *dataStoreYaml) load() error { return nil } +// GetUrls implements DataStore interface. +func (s *dataStoreYaml) GetUrls() ([]string, error) { + var result []string + if err := s.load(); err != nil { + return result, err + } + + for i := 0; i < len(s.data.CredentialStorage); i++ { + result = append(result, s.data.CredentialStorage[i].URL) + } + + return result, nil +} + +// GetKeys implements DataStore interface. +func (s *dataStoreYaml) GetKeys() ([]string, error) { + var result []string + if err := s.load(); err != nil { + return result, err + } + + for i := 0; i < len(s.data.KeyValueStorage); i++ { + result = append(result, s.data.KeyValueStorage[i].Key) + } + + return result, nil +} + // GetForURL implements DataStore interface. func (s *dataStoreYaml) GetForURL(url string) (CredentialsItem, error) { if err := s.load(); err != nil {