github.com/tommi2day/pwcli@v0.0.0-20240317203041-4d1177a5ab91/cmd/rootcmd.go (about) 1 // Package cmd commands 2 package cmd 3 4 import ( 5 "fmt" 6 "os" 7 "path" 8 "strings" 9 "time" 10 11 "github.com/tommi2day/gomodules/common" 12 13 "golang.org/x/exp/slices" 14 15 "github.com/tommi2day/gomodules/pwlib" 16 17 "github.com/mitchellh/go-homedir" 18 log "github.com/sirupsen/logrus" 19 "github.com/spf13/cobra" 20 "github.com/spf13/viper" 21 prefixed "github.com/x-cray/logrus-prefixed-formatter" 22 ) 23 24 var ( 25 keydir string 26 datadir string 27 app string 28 keypass string 29 cfgFile string 30 method string 31 pc *pwlib.PassConfig 32 debugFlag = false 33 infoFlag = false 34 noLogColorFlag = false 35 unitTestFlag = false 36 37 // RootCmd entry point to start 38 RootCmd = &cobra.Command{ 39 Use: "pwcli", 40 Short: "pwcli – Password generation and encryption Tools", 41 Long: ``, 42 SilenceErrors: true, 43 } 44 ) 45 46 const ( 47 // allows you to override any config values using 48 // env APP_MY_VAR = "MY_VALUE" 49 // e.g. export APP_LDAP_USERNAME test 50 // maps to ldap.username 51 configEnvPrefix = "PW" 52 configName = "pwcli" 53 configType = "yaml" 54 typeVault = "vault" 55 typeGopass = "gopass" 56 typeKMS = "kms" 57 typePlain = "plain" 58 typeEnc = "enc" 59 typeGPG = "gpg" 60 typeGO = "go" 61 typeOpenSSL = "openssl" 62 defaultType = "openssl" 63 ) 64 65 func init() { 66 cobra.OnInitialize(initConfig) 67 RootCmd.PersistentFlags().BoolVarP(&debugFlag, "debug", "", false, "verbose debug output") 68 RootCmd.PersistentFlags().BoolVarP(&infoFlag, "info", "", false, "reduced info output") 69 RootCmd.PersistentFlags().BoolVarP(&unitTestFlag, "unit-test", "", false, "redirect output for unit tests") 70 RootCmd.PersistentFlags().BoolVarP(&noLogColorFlag, "no-color", "", false, "disable colored log output") 71 RootCmd.PersistentFlags().StringVarP(&app, "app", "a", configName, "name of application") 72 RootCmd.PersistentFlags().StringVarP(&keydir, "keydir", "K", "", "directory of keys") 73 RootCmd.PersistentFlags().StringVarP(&datadir, "datadir", "D", "", "directory of password files") 74 RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file name") 75 RootCmd.PersistentFlags().StringVarP(&method, "method", "m", defaultType, "encryption method (openssl|go|gopass|enc|plain|vault)") 76 // don't have variables populated here 77 if err := viper.BindPFlags(RootCmd.PersistentFlags()); err != nil { 78 log.Fatal(err) 79 } 80 } 81 82 // Execute run application 83 func Execute() { 84 if err := RootCmd.Execute(); err != nil { 85 fmt.Println(err) 86 os.Exit(1) 87 } 88 } 89 90 func initConfig() { 91 viper.SetConfigType(configType) 92 viper.SetConfigName(configName) 93 if cfgFile == "" { 94 // Use config file from the flag. 95 // Find home directory. 96 home, err := homedir.Dir() 97 if err != nil { 98 fmt.Println(err) 99 os.Exit(1) 100 } 101 102 // Search config in $HOME/etc and current directory. 103 etc := path.Join(home, "etc") 104 viper.AddConfigPath(etc) 105 viper.AddConfigPath(".") 106 } else { 107 // set filename form cli 108 viper.SetConfigFile(cfgFile) 109 } 110 111 // env var overrides 112 viper.AutomaticEnv() // read in environment variables that match 113 viper.SetEnvPrefix(configEnvPrefix) 114 // env var `LDAP_USERNAME` will be mapped to `ldap.username` 115 viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 116 117 // If a config file is found, read it in. 118 haveConfig, err := processConfig() 119 120 // check flags 121 processFlags() 122 123 // logger settings 124 log.SetLevel(log.ErrorLevel) 125 switch { 126 case debugFlag: 127 // report function name 128 log.SetReportCaller(true) 129 log.SetLevel(log.DebugLevel) 130 case infoFlag: 131 log.SetLevel(log.InfoLevel) 132 } 133 134 logFormatter := &prefixed.TextFormatter{ 135 DisableColors: noLogColorFlag, 136 FullTimestamp: true, 137 TimestampFormat: time.RFC1123, 138 } 139 log.SetFormatter(logFormatter) 140 141 if unitTestFlag { 142 log.SetOutput(RootCmd.OutOrStdout()) 143 } 144 // debug config file 145 if haveConfig { 146 log.Debugf("found configfile %s", cfgFile) 147 } else { 148 log.Debugf("Error using %s config: %s", configType, err) 149 } 150 151 // validate method 152 if method == "" { 153 method = defaultType 154 log.Debugf("use default method %s ", defaultType) 155 } 156 if !slices.Contains(pwlib.Methods, method) { 157 fmt.Println("Invalid method:", method) 158 os.Exit(1) 159 } 160 if method == typeVault || method == typeKMS || method == typePlain || method == typeEnc { 161 keypass = "" 162 } 163 // set pwlib config 164 pc = pwlib.NewConfig(app, datadir, keydir, keypass, method) 165 } 166 167 // processConfig reads in config file and ENV variables if set. 168 func processConfig() (bool, error) { 169 err := viper.ReadInConfig() 170 haveConfig := false 171 if err == nil { 172 cfgFile = viper.ConfigFileUsed() 173 haveConfig = true 174 viper.Set("config", cfgFile) 175 a := viper.GetString("app") 176 if len(a) > 0 { 177 app = a 178 } 179 } 180 return haveConfig, err 181 } 182 183 func processFlags() { 184 if common.CmdFlagChanged(RootCmd, "debug") { 185 viper.Set("debug", debugFlag) 186 } 187 if common.CmdFlagChanged(RootCmd, "info") { 188 viper.Set("info", infoFlag) 189 } 190 if common.CmdFlagChanged(RootCmd, "no-color") { 191 viper.Set("no-color", noLogColorFlag) 192 } 193 if keydir == "" { 194 keydir = viper.GetString("keydir") 195 } 196 if datadir == "" { 197 datadir = viper.GetString("datadir") 198 } 199 keypass = viper.GetString("keypass") 200 debugFlag = viper.GetBool("debug") 201 infoFlag = viper.GetBool("info") 202 method = viper.GetString("method") 203 }