native_store.go 4.1 KB

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