github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/config/credentials/native_store.go (about)

     1  package credentials
     2  
     3  import (
     4  	"github.com/docker/cli/cli/config/types"
     5  	"github.com/docker/docker-credential-helpers/client"
     6  	"github.com/docker/docker-credential-helpers/credentials"
     7  )
     8  
     9  const (
    10  	remoteCredentialsPrefix = "docker-credential-"
    11  	tokenUsername           = "<token>"
    12  )
    13  
    14  // nativeStore implements a credentials store
    15  // using native keychain to keep credentials secure.
    16  // It piggybacks into a file store to keep users' emails.
    17  type nativeStore struct {
    18  	programFunc client.ProgramFunc
    19  	fileStore   Store
    20  }
    21  
    22  // NewNativeStore creates a new native store that
    23  // uses a remote helper program to manage credentials.
    24  func NewNativeStore(file store, helperSuffix string) Store {
    25  	name := remoteCredentialsPrefix + helperSuffix
    26  	return &nativeStore{
    27  		programFunc: client.NewShellProgramFunc(name),
    28  		fileStore:   NewFileStore(file),
    29  	}
    30  }
    31  
    32  // Erase removes the given credentials from the native store.
    33  func (c *nativeStore) Erase(serverAddress string) error {
    34  	if err := client.Erase(c.programFunc, serverAddress); err != nil {
    35  		return err
    36  	}
    37  
    38  	// Fallback to plain text store to remove email
    39  	return c.fileStore.Erase(serverAddress)
    40  }
    41  
    42  // Get retrieves credentials for a specific server from the native store.
    43  func (c *nativeStore) Get(serverAddress string) (types.AuthConfig, error) {
    44  	// load user email if it exist or an empty auth config.
    45  	auth, _ := c.fileStore.Get(serverAddress)
    46  
    47  	creds, err := c.getCredentialsFromStore(serverAddress)
    48  	if err != nil {
    49  		return auth, err
    50  	}
    51  	auth.Username = creds.Username
    52  	auth.IdentityToken = creds.IdentityToken
    53  	auth.Password = creds.Password
    54  
    55  	return auth, nil
    56  }
    57  
    58  // GetAll retrieves all the credentials from the native store.
    59  func (c *nativeStore) GetAll() (map[string]types.AuthConfig, error) {
    60  	auths, err := c.listCredentialsInStore()
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	// Emails are only stored in the file store.
    66  	// This call can be safely eliminated when emails are removed.
    67  	fileConfigs, _ := c.fileStore.GetAll()
    68  
    69  	authConfigs := make(map[string]types.AuthConfig)
    70  	for registry := range auths {
    71  		creds, err := c.getCredentialsFromStore(registry)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		ac := fileConfigs[registry] // might contain Email
    76  		ac.Username = creds.Username
    77  		ac.Password = creds.Password
    78  		ac.IdentityToken = creds.IdentityToken
    79  		authConfigs[registry] = ac
    80  	}
    81  
    82  	return authConfigs, nil
    83  }
    84  
    85  // Store saves the given credentials in the file store.
    86  func (c *nativeStore) Store(authConfig types.AuthConfig) error {
    87  	if err := c.storeCredentialsInStore(authConfig); err != nil {
    88  		return err
    89  	}
    90  	authConfig.Username = ""
    91  	authConfig.Password = ""
    92  	authConfig.IdentityToken = ""
    93  
    94  	// Fallback to old credential in plain text to save only the email
    95  	return c.fileStore.Store(authConfig)
    96  }
    97  
    98  // storeCredentialsInStore executes the command to store the credentials in the native store.
    99  func (c *nativeStore) storeCredentialsInStore(config types.AuthConfig) error {
   100  	creds := &credentials.Credentials{
   101  		ServerURL: config.ServerAddress,
   102  		Username:  config.Username,
   103  		Secret:    config.Password,
   104  	}
   105  
   106  	if config.IdentityToken != "" {
   107  		creds.Username = tokenUsername
   108  		creds.Secret = config.IdentityToken
   109  	}
   110  
   111  	return client.Store(c.programFunc, creds)
   112  }
   113  
   114  // getCredentialsFromStore executes the command to get the credentials from the native store.
   115  func (c *nativeStore) getCredentialsFromStore(serverAddress string) (types.AuthConfig, error) {
   116  	var ret types.AuthConfig
   117  
   118  	creds, err := client.Get(c.programFunc, serverAddress)
   119  	if err != nil {
   120  		if credentials.IsErrCredentialsNotFound(err) {
   121  			// do not return an error if the credentials are not
   122  			// in the keychain. Let docker ask for new credentials.
   123  			return ret, nil
   124  		}
   125  		return ret, err
   126  	}
   127  
   128  	if creds.Username == tokenUsername {
   129  		ret.IdentityToken = creds.Secret
   130  	} else {
   131  		ret.Password = creds.Secret
   132  		ret.Username = creds.Username
   133  	}
   134  
   135  	ret.ServerAddress = serverAddress
   136  	return ret, nil
   137  }
   138  
   139  // listCredentialsInStore returns a listing of stored credentials as a map of
   140  // URL -> username.
   141  func (c *nativeStore) listCredentialsInStore() (map[string]string, error) {
   142  	return client.List(c.programFunc)
   143  }