Skip to content

Commit

Permalink
Merge pull request #69 from allcloud-jonathan/feature/keychain_support
Browse files Browse the repository at this point in the history
Feature/keychain support
  • Loading branch information
Jonathan committed Dec 7, 2018
2 parents 675af8f + e59b700 commit 3bd7bcf
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 12 deletions.
18 changes: 18 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@
[[constraint]]
name = "github.com/mattn/go-colorable"
version = "0.0.9"

[[constraint]]
branch = "master"
name = "github.com/tmc/keyring"
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,15 @@ To save the credentials to a custom file, use the `-w` flag.
To print the credentials to the shell instead of storing them in a file, use the `-s` flag. This
will output shell commands which can be pasted in any shell to use the credentials.

### Storing the password in the keychain

> WARNING: Storing the password without having MFA enabled is a security risk. It allows anyone
> to assume your roles who has access to your computer.
Storing a password for a provider is as simple as running:

clisso providers passwd my-provider

### Selecting an App

You can **select** an app by using the following command:
Expand All @@ -278,6 +287,7 @@ apps using `clisso apps ls`.
## Caveats and Limitations

- No support for Okta applications with MFA enabled **at the application level**.
- No support for storing passwords on Windows.

## Contributing

Expand Down
27 changes: 27 additions & 0 deletions cmd/providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"sort"
"strconv"

"github.com/allcloud-io/clisso/keychain"
"github.com/fatih/color"
"github.com/howeyc/gopass"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -50,6 +52,7 @@ func init() {
// Build command tree
RootCmd.AddCommand(cmdProviders)
cmdProviders.AddCommand(cmdProvidersList)
cmdProviders.AddCommand(cmdProvidersPassword)
cmdProviders.AddCommand(cmdProvidersCreate)
cmdProvidersCreate.AddCommand(cmdProvidersCreateOneLogin)
cmdProvidersCreate.AddCommand(cmdProvidersCreateOkta)
Expand Down Expand Up @@ -85,6 +88,30 @@ var cmdProvidersList = &cobra.Command{
},
}

var cmdProvidersPassword = &cobra.Command{
Use: "passwd",
Short: "Save password in KeyChain for provider",
Long: "Save password in KeyChain for provider, see github.com/tmc/keyring for supported stores.",
Args: cobra.ExactArgs(1),

Run: func(cmd *cobra.Command, args []string) {
provider := args[0]
fmt.Printf("Please enter the password for the '%s' provider: ", provider)
pass, err := gopass.GetPasswd()
if err != nil {
log.Fatalf(color.RedString("Could not read password"))
}

keyChain := keychain.DefaultKeychain{}

err = keyChain.Set(provider, pass)
if err != nil {
fmt.Fatalf("Could not save to keychain: %+v", err)
}
log.Printf(color.GreenString("Saved password for Provider '%s'"), provider)
},
}

var cmdProvidersCreate = &cobra.Command{
Use: "create",
Short: "Create a new provider",
Expand Down
48 changes: 48 additions & 0 deletions keychain/keychain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package keychain

import (
"fmt"

"github.com/howeyc/gopass"
)

const (
// KeyChainName is the name of the keychain used to store
// passwords
KeyChainName = "clisso"
)

// Keychain provides an interface to allow for the easy testing
// of this package
type Keychain interface {
Get(string) ([]byte, error)
Set(string, []byte) error
}

// DefaultKeychain provides a wrapper around github.com/tmc/keyring
// and provides defaults and abstractions for clisso to get passwords
type DefaultKeychain struct{}

// Set takes a provider in an argument, and a password from STDIN, and
// sets it in a keychain, should one exist.
func (DefaultKeychain) Set(provider string, password []byte) (err error) {
return set(provider, password)
}

// Get will, once given a valid provider, return the password associated
// in order for logins to happen.
// If any error occours while talking to the keychain provider, we silently swallow it
// and just ask the user for the password instead. Error could be anything from access denied to
// password not found.
func (DefaultKeychain) Get(provider string) (pw []byte, err error) {
pass, err := get(provider)
if err != nil {
// If we ever implement a logfile we might want to log what error occurred.
fmt.Printf("Please enter %s password: ", provider)
pass, err = gopass.GetPasswd()
if err != nil {
return nil, fmt.Errorf("couldn't read password from terminal")
}
}
return pass, nil
}
15 changes: 15 additions & 0 deletions keychain/keychain_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// +build !windows

package keychain

import "github.com/tmc/keyring"

func set(provider string, password []byte) (err error) {
return keyring.Set(KeyChainName, provider, string(password))
}

func get(provider string) (pw []byte, err error) {
pwString, err := keyring.Get(KeyChainName, provider)
pw = []byte(pwString)
return
}
19 changes: 19 additions & 0 deletions keychain/keychain_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// +build windows

package keychain

import (
"errors"
"log"

"github.com/fatih/color"
)

func set(provider string, password []byte) (err error) {
log.Fatal(color.RedString("Storing passwords is not supported on windows"))
return
}

func get(provider string) (pw []byte, err error) {
return nil, errors.New("windows platform is not supported yet")
}
12 changes: 6 additions & 6 deletions okta/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import (

"github.com/allcloud-io/clisso/aws"
"github.com/allcloud-io/clisso/config"
"github.com/allcloud-io/clisso/keychain"
"github.com/allcloud-io/clisso/saml"
"github.com/allcloud-io/clisso/spinner"
"github.com/fatih/color"
"github.com/howeyc/gopass"
)

var (
keyChain = keychain.DefaultKeychain{}
)

// Get gets temporary credentials for the given app.
Expand Down Expand Up @@ -40,11 +44,7 @@ func Get(app, provider string, duration int64) (*aws.Credentials, error) {
fmt.Scanln(&user)
}

fmt.Print("Okta password: ")
pass, err := gopass.GetPasswd()
if err != nil {
return nil, fmt.Errorf("Couldn't read password from terminal")
}
pass, err := keyChain.Get(provider)

// Initialize spinner
var s = spinner.New()
Expand Down
12 changes: 6 additions & 6 deletions onelogin/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (

"github.com/allcloud-io/clisso/aws"
"github.com/allcloud-io/clisso/config"
"github.com/allcloud-io/clisso/keychain"
"github.com/allcloud-io/clisso/saml"
"github.com/allcloud-io/clisso/spinner"
"github.com/fatih/color"
"github.com/howeyc/gopass"
)

const (
Expand All @@ -28,6 +28,10 @@ const (
MFAInterval = 1
)

var (
keyChain = keychain.DefaultKeychain{}
)

// Get gets temporary credentials for the given app.
// TODO Move AWS logic outside this function.
func Get(app, provider string, duration int64) (*aws.Credentials, error) {
Expand Down Expand Up @@ -65,11 +69,7 @@ func Get(app, provider string, duration int64) (*aws.Credentials, error) {
fmt.Scanln(&user)
}

fmt.Print("OneLogin password: ")
pass, err := gopass.GetPasswd()
if err != nil {
return nil, fmt.Errorf("Couldn't read password from terminal")
}
pass, err := keyChain.Get(provider)

// Generate SAML assertion
pSAML := GenerateSamlAssertionParams{
Expand Down

0 comments on commit 3bd7bcf

Please sign in to comment.