github.com/nikkelma/oras-project_oras-go@v1.1.1-0.20220201001104-a75f6a419090/pkg/auth/docker/client.go (about) 1 /* 2 Copyright The ORAS Authors. 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package docker 17 18 import ( 19 "os" 20 21 "github.com/docker/cli/cli/config" 22 "github.com/docker/cli/cli/config/configfile" 23 "github.com/docker/cli/cli/config/credentials" 24 "github.com/pkg/errors" 25 26 "oras.land/oras-go/pkg/auth" 27 ) 28 29 // Client provides authentication operations for docker registries. 30 type Client struct { 31 configs []*configfile.ConfigFile 32 } 33 34 // NewClient creates a new auth client based on provided config paths. 35 // If not config path is provided, the default path is used. 36 // Credentials are read from the first config and fall backs to next. 37 // All changes will only be written to the first config file. 38 func NewClient(configPaths ...string) (auth.Client, error) { 39 if len(configPaths) == 0 { 40 cfg, err := config.Load(config.Dir()) 41 if err != nil { 42 return nil, err 43 } 44 if !cfg.ContainsAuth() { 45 cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore) 46 } 47 48 return &Client{ 49 configs: []*configfile.ConfigFile{cfg}, 50 }, nil 51 } 52 53 var configs []*configfile.ConfigFile 54 for _, path := range configPaths { 55 cfg, err := loadConfigFile(path) 56 if err != nil { 57 return nil, errors.Wrap(err, path) 58 } 59 configs = append(configs, cfg) 60 } 61 62 return &Client{ 63 configs: configs, 64 }, nil 65 } 66 67 // NewClientWithDockerFallback creates a new auth client 68 // which falls back on Docker's default config path. 69 // This allows support for ~/.docker/config.json as a fallback, 70 // as well as support for the DOCKER_CONFIG environment variable. 71 func NewClientWithDockerFallback(configPaths ...string) (auth.Client, error) { 72 if len(configPaths) == 0 { 73 return NewClient() 74 } 75 76 var configs []*configfile.ConfigFile 77 for _, path := range configPaths { 78 cfg, err := loadConfigFile(path) 79 if err != nil { 80 return nil, errors.Wrap(err, path) 81 } 82 configs = append(configs, cfg) 83 } 84 85 // Add the Docker default config last 86 dockerFallbackCfg, err := config.Load(config.Dir()) 87 if err != nil { 88 return nil, err 89 } 90 if !dockerFallbackCfg.ContainsAuth() { 91 dockerFallbackCfg.CredentialsStore = credentials.DetectDefaultStore(dockerFallbackCfg.CredentialsStore) 92 } 93 configs = append(configs, dockerFallbackCfg) 94 95 return &Client{ 96 configs: configs, 97 }, nil 98 } 99 100 func (c *Client) primaryCredentialsStore(hostname string) credentials.Store { 101 return c.configs[0].GetCredentialsStore(hostname) 102 } 103 104 // loadConfigFile reads the configuration files from the given path. 105 func loadConfigFile(path string) (*configfile.ConfigFile, error) { 106 cfg := configfile.New(path) 107 if _, err := os.Stat(path); err == nil { 108 file, err := os.Open(path) 109 if err != nil { 110 return nil, err 111 } 112 defer file.Close() 113 if err := cfg.LoadFromReader(file); err != nil { 114 return nil, err 115 } 116 } else if !os.IsNotExist(err) { 117 return nil, err 118 } 119 if !cfg.ContainsAuth() { 120 cfg.CredentialsStore = credentials.DetectDefaultStore(cfg.CredentialsStore) 121 } 122 return cfg, nil 123 }