1
0
mirror of https://github.com/minio/mc.git synced 2025-11-13 12:22:45 +03:00
Files
mc/cmd-config.go

282 lines
7.5 KiB
Go

/*
* Mini Copy, (C) 2014, 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package main
import (
"net/url"
"os"
"path"
"runtime"
"strings"
"os/user"
"path/filepath"
"github.com/minio-io/cli"
"github.com/minio-io/mc/pkg/console"
"github.com/minio-io/mc/pkg/qdb"
"github.com/minio-io/minio/pkg/iodine"
"github.com/minio-io/minio/pkg/utils/log"
)
const (
mcConfigDir = ".mc/"
mcConfigWindowsDir = "mc/"
mcConfigFile = "config.json"
)
var (
mcCurrentConfigVersion = qdb.Version{Major: 1, Minor: 0, Patch: 0}
)
const (
// do not pass accesskeyid and secretaccesskey through cli
// users should manually edit them, add a stub code
accessKeyID = "YOUR-ACCESS-KEY-ID-HERE"
secretAccesskey = "YOUR-SECRET-ACCESS-KEY-HERE"
)
func getMcConfigDir() (string, error) {
u, err := user.Current()
if err != nil {
return "", iodine.New(err, nil)
}
// For windows the path is slightly different
switch runtime.GOOS {
case "windows":
return path.Join(u.HomeDir, mcConfigWindowsDir), nil
default:
return path.Join(u.HomeDir, mcConfigDir), nil
}
}
func createMcConfigDir() error {
p, err := getMcConfigDir()
if err != nil {
return iodine.New(err, nil)
}
err = os.MkdirAll(p, 0700)
if err != nil {
return iodine.New(err, nil)
}
return nil
}
func getMcConfigPath() (string, error) {
dir, err := getMcConfigDir()
if err != nil {
return "", iodine.New(err, nil)
}
return path.Join(dir, mcConfigFile), nil
}
func mustGetMcConfigPath() string {
p, _ := getMcConfigPath()
return p
}
// getMcConfig returns the config
func getMcConfig() (config qdb.Store, err error) {
if !isMcConfigExist() {
return nil, iodine.New(errInvalidArgument{}, nil)
}
configFile, err := getMcConfigPath()
if err != nil {
return nil, iodine.New(err, nil)
}
if configStore := qdb.NewStore(mcCurrentConfigVersion); configStore != nil {
if err := configStore.Load(configFile); err != nil {
return nil, iodine.New(err, nil)
}
return configStore, nil
}
return nil, iodine.New(errInvalidArgument{}, nil)
}
// isMcConfigExist returns true/false if config exists
func isMcConfigExist() bool {
configFile, err := getMcConfigPath()
if err != nil {
return false
}
_, err = os.Stat(configFile)
if err != nil {
return false
}
return true
}
// writeConfig
func writeConfig(config qdb.Store) error {
err := createMcConfigDir()
if err != nil {
return iodine.New(err, nil)
}
configPath, err := getMcConfigPath()
if err != nil {
return iodine.New(err, nil)
}
if err := config.Save(configPath); err != nil {
return iodine.New(err, nil)
}
return nil
}
// saveConfig writes configuration data in json format to config file.
func saveConfig(ctx *cli.Context) error {
switch ctx.Args().Get(0) {
case "generate":
if isMcConfigExist() {
return iodine.New(errConfigExists{}, nil)
}
err := writeConfig(newConfig())
if err != nil {
return iodine.New(err, nil)
}
return nil
case "check":
// verify if the binary can load config file
configStore, err := getMcConfig()
if err != nil || configStore == nil {
return iodine.New(err, nil)
}
return nil
default:
configStore, err := parseConfigInput(ctx)
if err != nil {
return iodine.New(err, nil)
}
return writeConfig(configStore)
}
}
func newConfig() (config qdb.Store) {
configStore := qdb.NewStore(mcCurrentConfigVersion)
s3Auth := make(map[string]string)
localAuth := make(map[string]string)
hosts := make(map[string]map[string]string)
s3Auth["Auth.AccessKeyID"] = accessKeyID
s3Auth["Auth.SecretAccessKey"] = secretAccesskey
hosts["http*://s3*.amazonaws.com"] = s3Auth
// local minio server can have this empty until webcli is ready
// which would make it easier to generate accesskeys and manage
localAuth["Auth.AccessKeyID"] = ""
localAuth["Auth.SecretAccessKey"] = ""
hosts["http*://localhost:*"] = localAuth
configStore.SetMapMapString("Hosts", hosts)
aliases := make(map[string]string)
aliases["s3"] = "https://s3.amazonaws.com"
aliases["localhost"] = "http://localhost:9000"
configStore.SetMapString("Aliases", aliases)
return configStore
}
func parseConfigInput(ctx *cli.Context) (config qdb.Store, err error) {
configStore := qdb.NewStore(mcCurrentConfigVersion)
configStore.Load(mcConfigFile)
alias := strings.Fields(ctx.String("alias"))
switch true {
case len(alias) == 2:
aliasName := alias[0]
url := strings.TrimSuffix(alias[1], "/")
if strings.HasPrefix(aliasName, "http") {
return nil, iodine.New(errInvalidAliasName{name: aliasName}, nil)
}
if !strings.HasPrefix(url, "http") {
return nil, iodine.New(errInvalidURL{url: url}, nil)
}
if !isValidAliasName(aliasName) {
return nil, iodine.New(errInvalidAliasName{name: aliasName}, nil)
}
aliases := configStore.GetMapString("Aliases")
if _, ok := aliases[aliasName]; ok {
return nil, iodine.New(errAliasExists{name: aliasName}, nil)
}
aliases[aliasName] = url
configStore.SetMapString("Aliases", aliases)
return configStore, nil
default:
return nil, iodine.New(errInvalidArgument{}, nil)
}
}
// getHostURL -
func getHostURL(u *url.URL) string {
return u.Scheme + "://" + u.Host
}
// getHostConfig retrieves host specific configuration such as access keys, certs.
func getHostConfig(requestURL string) (map[string]string, error) {
u, err := url.Parse(requestURL)
if err != nil {
return nil, iodine.New(errInvalidURL{url: requestURL}, nil)
}
config, err := getMcConfig()
if err != nil {
return nil, iodine.New(err, nil)
}
for globURL, hostConfig := range config.GetMapMapString("Hosts") {
match, err := filepath.Match(globURL, getHostURL(u))
if err != nil {
return nil, iodine.New(errInvalidGlobURL{glob: globURL, request: requestURL}, nil)
}
if match {
// verify Auth key validity for all hosts other than localhost
if !strings.Contains(getHostURL(u), "localhost") {
if !isValidAccessKey(hostConfig["Auth.AccessKeyID"]) {
return nil, iodine.New(errInvalidAuthKeys{}, nil)
}
if !isValidSecretKey(hostConfig["Auth.SecretAccessKey"]) {
return nil, iodine.New(errInvalidAuthKeys{}, nil)
}
}
return hostConfig, nil
}
}
return nil, iodine.New(errNoMatchingHost{}, nil)
}
// doConfigCmd is the handler for "mc config" sub-command.
func doConfigCmd(ctx *cli.Context) {
// show help if nothing is set
if len(ctx.Args()) < 1 && !ctx.IsSet("completion") && !ctx.IsSet("alias") {
cli.ShowCommandHelpAndExit(ctx, "config", 1) // last argument is exit code
}
configPath, err := getMcConfigPath()
if err != nil {
log.Debug.Println(iodine.New(err, nil))
console.Fatalln("mc: Unable to identify config file path")
}
err = saveConfig(ctx)
switch iodine.ToError(err).(type) {
case errConfigExists:
log.Debug.Println(iodine.New(err, nil))
console.Fatalln("mc: Configuration file " + configPath + " already exists")
default:
// unexpected error
log.Debug.Println(iodine.New(err, nil))
console.Fatalln("mc: Unable to generate config file", configPath)
}
console.Infoln("mc: Configuration written to " + configPath + ". Please update your access credentials.")
}