2016-02-07 19:55:17 -05:00
|
|
|
package credentials
|
|
|
|
|
|
|
|
import (
|
2016-06-04 13:08:22 -04:00
|
|
|
"github.com/docker/docker-credential-helpers/client"
|
|
|
|
"github.com/docker/docker-credential-helpers/credentials"
|
2016-04-21 18:37:08 -04:00
|
|
|
"github.com/docker/docker/cliconfig/configfile"
|
2016-02-07 19:55:17 -05:00
|
|
|
"github.com/docker/engine-api/types"
|
|
|
|
)
|
|
|
|
|
2016-03-04 15:00:18 -05:00
|
|
|
const (
|
|
|
|
remoteCredentialsPrefix = "docker-credential-"
|
|
|
|
tokenUsername = "<token>"
|
|
|
|
)
|
2016-02-07 19:55:17 -05:00
|
|
|
|
|
|
|
// nativeStore implements a credentials store
|
|
|
|
// using native keychain to keep credentials secure.
|
|
|
|
// It piggybacks into a file store to keep users' emails.
|
|
|
|
type nativeStore struct {
|
2016-06-04 13:08:22 -04:00
|
|
|
programFunc client.ProgramFunc
|
|
|
|
fileStore Store
|
2016-02-07 19:55:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewNativeStore creates a new native store that
|
|
|
|
// uses a remote helper program to manage credentials.
|
2016-04-21 18:37:08 -04:00
|
|
|
func NewNativeStore(file *configfile.ConfigFile) Store {
|
2016-06-04 13:08:22 -04:00
|
|
|
name := remoteCredentialsPrefix + file.CredentialsStore
|
2016-02-07 19:55:17 -05:00
|
|
|
return &nativeStore{
|
2016-06-04 13:08:22 -04:00
|
|
|
programFunc: client.NewShellProgramFunc(name),
|
|
|
|
fileStore: NewFileStore(file),
|
2016-02-07 19:55:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Erase removes the given credentials from the native store.
|
|
|
|
func (c *nativeStore) Erase(serverAddress string) error {
|
2016-06-04 13:08:22 -04:00
|
|
|
if err := client.Erase(c.programFunc, serverAddress); err != nil {
|
2016-02-07 19:55:17 -05:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback to plain text store to remove email
|
|
|
|
return c.fileStore.Erase(serverAddress)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get retrieves credentials for a specific server from the native store.
|
|
|
|
func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) {
|
|
|
|
// load user email if it exist or an empty auth config.
|
|
|
|
auth, _ := c.fileStore.Get(serverAddress)
|
|
|
|
|
|
|
|
creds, err := c.getCredentialsFromStore(serverAddress)
|
|
|
|
if err != nil {
|
|
|
|
return auth, err
|
|
|
|
}
|
|
|
|
auth.Username = creds.Username
|
2016-03-04 15:00:18 -05:00
|
|
|
auth.IdentityToken = creds.IdentityToken
|
2016-02-07 19:55:17 -05:00
|
|
|
auth.Password = creds.Password
|
|
|
|
|
|
|
|
return auth, nil
|
|
|
|
}
|
|
|
|
|
2016-03-01 12:04:35 -05:00
|
|
|
// GetAll retrieves all the credentials from the native store.
|
|
|
|
func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) {
|
|
|
|
auths, _ := c.fileStore.GetAll()
|
|
|
|
|
|
|
|
for s, ac := range auths {
|
|
|
|
creds, _ := c.getCredentialsFromStore(s)
|
|
|
|
ac.Username = creds.Username
|
|
|
|
ac.Password = creds.Password
|
2016-03-04 15:00:18 -05:00
|
|
|
ac.IdentityToken = creds.IdentityToken
|
2016-03-01 12:04:35 -05:00
|
|
|
auths[s] = ac
|
|
|
|
}
|
|
|
|
|
|
|
|
return auths, nil
|
|
|
|
}
|
|
|
|
|
2016-02-07 19:55:17 -05:00
|
|
|
// Store saves the given credentials in the file store.
|
|
|
|
func (c *nativeStore) Store(authConfig types.AuthConfig) error {
|
|
|
|
if err := c.storeCredentialsInStore(authConfig); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
authConfig.Username = ""
|
|
|
|
authConfig.Password = ""
|
2016-03-04 15:00:18 -05:00
|
|
|
authConfig.IdentityToken = ""
|
2016-02-07 19:55:17 -05:00
|
|
|
|
|
|
|
// Fallback to old credential in plain text to save only the email
|
|
|
|
return c.fileStore.Store(authConfig)
|
|
|
|
}
|
|
|
|
|
|
|
|
// storeCredentialsInStore executes the command to store the credentials in the native store.
|
|
|
|
func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error {
|
2016-06-04 13:08:22 -04:00
|
|
|
creds := &credentials.Credentials{
|
2016-02-07 19:55:17 -05:00
|
|
|
ServerURL: config.ServerAddress,
|
|
|
|
Username: config.Username,
|
2016-03-04 15:00:18 -05:00
|
|
|
Secret: config.Password,
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.IdentityToken != "" {
|
|
|
|
creds.Username = tokenUsername
|
|
|
|
creds.Secret = config.IdentityToken
|
2016-02-07 19:55:17 -05:00
|
|
|
}
|
|
|
|
|
2016-06-04 13:08:22 -04:00
|
|
|
return client.Store(c.programFunc, creds)
|
2016-02-07 19:55:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// getCredentialsFromStore executes the command to get the credentials from the native store.
|
|
|
|
func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) {
|
|
|
|
var ret types.AuthConfig
|
|
|
|
|
2016-06-04 13:08:22 -04:00
|
|
|
creds, err := client.Get(c.programFunc, serverAddress)
|
2016-02-07 19:55:17 -05:00
|
|
|
if err != nil {
|
2016-06-04 13:08:22 -04:00
|
|
|
if credentials.IsErrCredentialsNotFound(err) {
|
|
|
|
// do not return an error if the credentials are not
|
|
|
|
// in the keyckain. Let docker ask for new credentials.
|
2016-02-07 19:55:17 -05:00
|
|
|
return ret, nil
|
|
|
|
}
|
|
|
|
return ret, err
|
|
|
|
}
|
|
|
|
|
2016-06-04 13:08:22 -04:00
|
|
|
if creds.Username == tokenUsername {
|
|
|
|
ret.IdentityToken = creds.Secret
|
2016-03-04 15:00:18 -05:00
|
|
|
} else {
|
2016-06-04 13:08:22 -04:00
|
|
|
ret.Password = creds.Secret
|
|
|
|
ret.Username = creds.Username
|
2016-03-04 15:00:18 -05:00
|
|
|
}
|
|
|
|
|
2016-02-07 19:55:17 -05:00
|
|
|
ret.ServerAddress = serverAddress
|
|
|
|
return ret, nil
|
|
|
|
}
|