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 }