From 673c8d0faaa1ee826d977f53ac2612ea02831a1d Mon Sep 17 00:00:00 2001 From: Isaac Boaz Date: Wed, 3 Jul 2024 15:17:41 -0700 Subject: [PATCH] Format all code (#1136) --- atrium/buildopts/argosyopts/options_common.go | 24 +- atrium/buildopts/argosyopts/options_stub.go | 24 +- .../speculatio/spiralis/ttdirender/element.go | 2 +- atrium/trcdb/xdbutil/xdbmanager.go | 108 +- .../vestibulum/trcsh/kube/native/trckube.go | 6 +- atrium/vestibulum/trcsh/trcshmemfs.go | 4 +- pkg/cli/trcxbase/trcxbase.go | 1652 ++++++++--------- pkg/trcinit/initlib/vault-seed.go | 4 +- pkg/trcx/xencrypt/xencrypt.go | 4 +- pkg/utils/defaultTemplateParser.go | 78 +- pkg/utils/diffUtil.go | 1180 ++++++------ pkg/utils/mlock/mlock_none.go | 82 +- pkg/utils/mlock/mlock_unix.go | 100 +- pkg/utils/versionUtil.go | 458 ++--- pkg/validator/keystore.go | 322 ++-- pkg/vaulthelper/kv/cert.go | 28 +- pkg/vaulthelper/system/AppRole.go | 438 ++--- pkg/vaulthelper/system/Vault.go | 1142 ++++++------ trcweb/client/client.go | 82 +- trcweb/graphQL/gql.go | 632 +++---- trcweb/rpc/apinator/service.pb.go | 24 +- trcweb/rpc/apinator/service.twirp.go | 5 +- trcweb/server/InitVault.go | 6 +- trcweb/server/VaultInit.go | 526 +++--- trcweb/server/gqlTemplates.go | 396 ++-- trcweb/server/session.go | 334 ++-- trcweb/server/sessionq.go | 172 +- trcweb/server/sessions.go | 6 +- trcweb/server/tokens.go | 248 +-- 29 files changed, 4045 insertions(+), 4042 deletions(-) diff --git a/atrium/buildopts/argosyopts/options_common.go b/atrium/buildopts/argosyopts/options_common.go index 486e47f7f..ed2b0d197 100644 --- a/atrium/buildopts/argosyopts/options_common.go +++ b/atrium/buildopts/argosyopts/options_common.go @@ -16,18 +16,18 @@ var data []string = []string{"One", "Two", "Three", "Four", "Five", // using tests from 8/24/22 var TimeData = map[string][]float64{ - data[0]: []float64{0.0, .650, .95, 5.13, 317.85, 317.85}, - data[1]: []float64{0.0, 0.3, 0.56, 5.06, 78.4, 78.4}, - data[2]: []float64{0.0, 0.2, 0.38, 5.33, 78.4, 78.4}, - data[3]: []float64{0.0, 0.34, 0.36, 5.25, 141.93, 141.93}, - data[4]: []float64{0.0, 0.24, 0.52, 4.87, 141.91, 141.91}, - data[5]: []float64{0.0, 0.24, 0.6, 5.39, 148.01, 148.01}, - data[6]: []float64{0.0, 0.11, 0.13, 4.89, 32.47, 32.47}, - data[7]: []float64{0.0, 0.08, 0.1, 4.82, 32.49, 32.49}, - data[8]: []float64{0.0, 0.33, 0.5, 5.21, 89.53, 89.53}, - data[9]: []float64{0.0, 0.3, 0.62, 5, 599.99}, //when test fails no repeat at end - data[10]: []float64{0.0, 0.19, 0.47, 4.87, 38.5, 38.5}, - data[11]: []float64{0.0, 0.26, 0.58, 5, 39.08, 39.08}, + data[0]: {0.0, .650, .95, 5.13, 317.85, 317.85}, + data[1]: {0.0, 0.3, 0.56, 5.06, 78.4, 78.4}, + data[2]: {0.0, 0.2, 0.38, 5.33, 78.4, 78.4}, + data[3]: {0.0, 0.34, 0.36, 5.25, 141.93, 141.93}, + data[4]: {0.0, 0.24, 0.52, 4.87, 141.91, 141.91}, + data[5]: {0.0, 0.24, 0.6, 5.39, 148.01, 148.01}, + data[6]: {0.0, 0.11, 0.13, 4.89, 32.47, 32.47}, + data[7]: {0.0, 0.08, 0.1, 4.82, 32.49, 32.49}, + data[8]: {0.0, 0.33, 0.5, 5.21, 89.53, 89.53}, + data[9]: {0.0, 0.3, 0.62, 5, 599.99}, //when test fails no repeat at end + data[10]: {0.0, 0.19, 0.47, 4.87, 38.5, 38.5}, + data[11]: {0.0, 0.26, 0.58, 5, 39.08, 39.08}, } // GetStubbedDataFlowStatistics returns the list data being tracked along with time data for the data being tracked. diff --git a/atrium/buildopts/argosyopts/options_stub.go b/atrium/buildopts/argosyopts/options_stub.go index 661772c69..6d9ad99db 100644 --- a/atrium/buildopts/argosyopts/options_stub.go +++ b/atrium/buildopts/argosyopts/options_stub.go @@ -19,18 +19,18 @@ var data []string = []string{"One", "Two", "Three", "Four", "Five", "Ten", "Eleven", "Twelve"} var TimeData = map[string][]float64{ - data[0]: []float64{0.0, .650, .95, 5.13, 317.85, 317.85}, - data[1]: []float64{0.0, 0.3, 0.56, 5.06, 78.4, 78.4}, - data[2]: []float64{0.0, 0.2, 0.38, 5.33, 78.4, 78.4}, - data[3]: []float64{0.0, 0.34, 0.36, 5.25, 141.93, 141.93}, - data[4]: []float64{0.0, 0.24, 0.52, 4.87, 141.91, 141.91}, - data[5]: []float64{0.0, 0.24, 0.6, 5.39, 148.01, 148.01}, - data[6]: []float64{0.0, 0.11, 0.13, 4.89, 32.47, 32.47}, - data[7]: []float64{0.0, 0.08, 0.1, 4.82, 32.49, 32.49}, - data[8]: []float64{0.0, 0.33, 0.5, 5.21, 89.53, 89.53}, - data[9]: []float64{0.0, 0.3, 0.62, 5, 599.99}, //when test fails no repeat at end - data[10]: []float64{0.0, 0.19, 0.47, 4.87, 38.5, 38.5}, - data[11]: []float64{0.0, 0.26, 0.58, 5, 39.08, 39.08}, + data[0]: {0.0, .650, .95, 5.13, 317.85, 317.85}, + data[1]: {0.0, 0.3, 0.56, 5.06, 78.4, 78.4}, + data[2]: {0.0, 0.2, 0.38, 5.33, 78.4, 78.4}, + data[3]: {0.0, 0.34, 0.36, 5.25, 141.93, 141.93}, + data[4]: {0.0, 0.24, 0.52, 4.87, 141.91, 141.91}, + data[5]: {0.0, 0.24, 0.6, 5.39, 148.01, 148.01}, + data[6]: {0.0, 0.11, 0.13, 4.89, 32.47, 32.47}, + data[7]: {0.0, 0.08, 0.1, 4.82, 32.49, 32.49}, + data[8]: {0.0, 0.33, 0.5, 5.21, 89.53, 89.53}, + data[9]: {0.0, 0.3, 0.62, 5, 599.99}, //when test fails no repeat at end + data[10]: {0.0, 0.19, 0.47, 4.87, 38.5, 38.5}, + data[11]: {0.0, 0.26, 0.58, 5, 39.08, 39.08}, } var pointer int diff --git a/atrium/speculatio/spiralis/ttdirender/element.go b/atrium/speculatio/spiralis/ttdirender/element.go index ded26d939..c1e837208 100644 --- a/atrium/speculatio/spiralis/ttdirender/element.go +++ b/atrium/speculatio/spiralis/ttdirender/element.go @@ -246,7 +246,7 @@ func (er *ElementRenderer) InitRenderLoop(worldApp *g3nworld.WorldApp) bool { for k, v := range er.LocationCache { copyCache[k] = v } - for key, _ := range copyCache { + for key := range copyCache { element := worldApp.ConcreteElements[key] if element.GetDetailedElement().Genre != "Solid" && element.GetDetailedElement().Name != "TenantDataBase" { er.initLocnCache(worldApp, element) diff --git a/atrium/trcdb/xdbutil/xdbmanager.go b/atrium/trcdb/xdbutil/xdbmanager.go index e43e2b13b..ce6cc4d1c 100644 --- a/atrium/trcdb/xdbutil/xdbmanager.go +++ b/atrium/trcdb/xdbutil/xdbmanager.go @@ -1,54 +1,54 @@ -package xdbutil - -import ( - "os" - - trcdb "github.com/trimble-oss/tierceron/atrium/trcdb" - "github.com/trimble-oss/tierceron/pkg/trcx/xutil" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" -) - -// GenerateSeedsFromVaultToDb pulls all data from vault for each template into a database -func GenerateSeedsFromVaultToDb(driverConfig *eUtils.DriverConfig) (interface{}, error) { - if driverConfig.Diff { //Clean flag in trcx - _, err1 := os.Stat(driverConfig.EndDir + driverConfig.Env) - err := os.RemoveAll(driverConfig.EndDir + driverConfig.Env) - - if err != nil { - eUtils.LogErrorObject(&driverConfig.CoreConfig, err, false) - return nil, err - } - - if err1 == nil { - eUtils.LogInfo(&driverConfig.CoreConfig, "Seed removed from"+driverConfig.EndDir+driverConfig.Env) - } - return nil, nil - } - - // Get files from directory - tempTemplatePaths := []string{} - for _, startDir := range driverConfig.StartDir { - //get files from directory - tp := xutil.GetDirFiles(startDir) - tempTemplatePaths = append(tempTemplatePaths, tp...) - } - - //Duplicate path remover - keys := make(map[string]bool) - templatePaths := []string{} - for _, path := range tempTemplatePaths { - if _, value := keys[path]; !value { - keys[path] = true - templatePaths = append(templatePaths, path) - } - } - - tierceronEngine, err := trcdb.CreateEngine(driverConfig, - templatePaths, driverConfig.Env, driverConfig.VersionFilter[0]) - if err != nil { - eUtils.LogErrorObject(&driverConfig.CoreConfig, err, false) - return nil, err - } - - return tierceronEngine, nil -} +package xdbutil + +import ( + "os" + + trcdb "github.com/trimble-oss/tierceron/atrium/trcdb" + "github.com/trimble-oss/tierceron/pkg/trcx/xutil" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" +) + +// GenerateSeedsFromVaultToDb pulls all data from vault for each template into a database +func GenerateSeedsFromVaultToDb(driverConfig *eUtils.DriverConfig) (interface{}, error) { + if driverConfig.Diff { //Clean flag in trcx + _, err1 := os.Stat(driverConfig.EndDir + driverConfig.Env) + err := os.RemoveAll(driverConfig.EndDir + driverConfig.Env) + + if err != nil { + eUtils.LogErrorObject(&driverConfig.CoreConfig, err, false) + return nil, err + } + + if err1 == nil { + eUtils.LogInfo(&driverConfig.CoreConfig, "Seed removed from"+driverConfig.EndDir+driverConfig.Env) + } + return nil, nil + } + + // Get files from directory + tempTemplatePaths := []string{} + for _, startDir := range driverConfig.StartDir { + //get files from directory + tp := xutil.GetDirFiles(startDir) + tempTemplatePaths = append(tempTemplatePaths, tp...) + } + + //Duplicate path remover + keys := make(map[string]bool) + templatePaths := []string{} + for _, path := range tempTemplatePaths { + if _, value := keys[path]; !value { + keys[path] = true + templatePaths = append(templatePaths, path) + } + } + + tierceronEngine, err := trcdb.CreateEngine(driverConfig, + templatePaths, driverConfig.Env, driverConfig.VersionFilter[0]) + if err != nil { + eUtils.LogErrorObject(&driverConfig.CoreConfig, err, false) + return nil, err + } + + return tierceronEngine, nil +} diff --git a/atrium/vestibulum/trcsh/kube/native/trckube.go b/atrium/vestibulum/trcsh/kube/native/trckube.go index d7f949715..0f7165624 100644 --- a/atrium/vestibulum/trcsh/kube/native/trckube.go +++ b/atrium/vestibulum/trcsh/kube/native/trckube.go @@ -26,9 +26,9 @@ import ( kubectlutil "k8s.io/kubectl/pkg/util" "github.com/go-git/go-billy/v5" + trcshMemFs "github.com/trimble-oss/tierceron/atrium/vestibulum/trcsh" "github.com/trimble-oss/tierceron/atrium/vestibulum/trcsh/kube/native/path" "github.com/trimble-oss/tierceron/atrium/vestibulum/trcsh/kube/native/trccreate" - trcshMemFs "github.com/trimble-oss/tierceron/atrium/vestibulum/trcsh" "github.com/trimble-oss/tierceron/pkg/capauth" "github.com/trimble-oss/tierceron/pkg/core" eUtils "github.com/trimble-oss/tierceron/pkg/utils" @@ -125,7 +125,7 @@ func ParseTrcKubeContext(trcKubeContext *TrcKubeContext, deployArgs []string) *T trcKubeContext = &TrcKubeContext{} } - for i, _ := range deployArgs { + for i := range deployArgs { if deployArgs[i] == "set-context" { if i+1 < len(deployArgs) { trcKubeContext.Context = deployArgs[i+1] @@ -161,7 +161,7 @@ func ParseTrcKubeDeployDirective(trcKubeDirective *TrcKubeDirective, deployArgs trcKubeDirective.Action = deployArgs[0] deployArgs = deployArgs[1:] - for i, _ := range deployArgs { + for i := range deployArgs { if trcKubeDirective.Action == "create" && (deployArgs[i] == "secret" || deployArgs[i] == "configmap") { trcKubeDirective.Object = deployArgs[i] if i+1 < len(deployArgs) { diff --git a/atrium/vestibulum/trcsh/trcshmemfs.go b/atrium/vestibulum/trcsh/trcshmemfs.go index 477c26aa9..5c9a942f9 100644 --- a/atrium/vestibulum/trcsh/trcshmemfs.go +++ b/atrium/vestibulum/trcsh/trcshmemfs.go @@ -15,7 +15,7 @@ type TrcshMemFs struct { BillyFs billy.Filesystem } -func (t *TrcshMemFs)WriteToMemFile(driverConfig *eUtils.DriverConfig, memCacheLocal *sync.Mutex, byteData *[]byte, path string){ +func (t *TrcshMemFs) WriteToMemFile(driverConfig *eUtils.DriverConfig, memCacheLocal *sync.Mutex, byteData *[]byte, path string) { configMemFs := driverConfig.MemFs.(*TrcshMemFs) @@ -37,4 +37,4 @@ func (t *TrcshMemFs)WriteToMemFile(driverConfig *eUtils.DriverConfig, memCacheLo eUtils.LogInfo(&driverConfig.CoreConfig, "Unexpected memfile exists:"+path) eUtils.CheckError(&driverConfig.CoreConfig, err, true) } -} \ No newline at end of file +} diff --git a/pkg/cli/trcxbase/trcxbase.go b/pkg/cli/trcxbase/trcxbase.go index 9dd511014..ed9c962d4 100644 --- a/pkg/cli/trcxbase/trcxbase.go +++ b/pkg/cli/trcxbase/trcxbase.go @@ -1,826 +1,826 @@ -package trcxbase - -import ( - "errors" - "flag" - "fmt" - "log" - "os" - "strings" - "sync" - "time" - - "github.com/trimble-oss/tierceron/buildopts/coreopts" - "github.com/trimble-oss/tierceron/buildopts/memonly" - "github.com/trimble-oss/tierceron/buildopts/memprotectopts" - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - - "github.com/hashicorp/vault/api" -) - -func messenger(configCtx *eUtils.ConfigContext, inData *string, inPath string) { - var data eUtils.ResultData - data.InData = inData - data.InPath = inPath - configCtx.ResultChannel <- &data -} - -func receiver(configCtx *eUtils.ConfigContext) { - for { - select { - case data := <-configCtx.ResultChannel: - if data != nil && data.InData != nil && data.InPath != "" { - configCtx.Mutex.Lock() - configCtx.ResultMap[data.InPath] = data.InData - configCtx.Mutex.Unlock() - } - } - } -} - -// CommonMain This executable automates the creation of seed files from template file(s). -// New seed files are written (or overwrite current seed files) to the specified directory. -func CommonMain(ctx eUtils.ProcessContext, - configDriver eUtils.ConfigDriver, - envPtr *string, - addrPtrIn *string, - envCtxPtr *string, - insecurePtrIn *bool, - flagset *flag.FlagSet, - argLines []string) { - // Executable input arguments(flags) - if flagset == nil { - flagset = flag.NewFlagSet(argLines[0], flag.ExitOnError) - flagset.Usage = func() { - fmt.Fprintf(flagset.Output(), "Usage of %s:\n", argLines[0]) - flagset.PrintDefaults() - } - flagset.String("env", "dev", "Environment to configure") - } - addrPtr := flagset.String("addr", "", "API endpoint for the vault") - if addrPtrIn != nil && *addrPtrIn != "" { - addrPtr = addrPtrIn - } - - startDirPtr := flagset.String("startDir", coreopts.BuildOptions.GetFolderPrefix(nil)+"_templates", "Pull templates from this directory") - endDirPtr := flagset.String("endDir", "./"+coreopts.BuildOptions.GetFolderPrefix(nil)+"_seeds/", "Write generated seed files to this directory") - logFilePtr := flagset.String("log", "./"+coreopts.BuildOptions.GetFolderPrefix(nil)+"x.log", "Output path for log file") - helpPtr := flagset.Bool("h", false, "Provide options for "+coreopts.BuildOptions.GetFolderPrefix(nil)+"x") - tokenPtr := flagset.String("token", "", "Vault access token") - secretMode := flagset.Bool("secretMode", true, "Only override secret values in templates?") - genAuth := flagset.Bool("genAuth", false, "Generate auth section of seed data?") - cleanPtr := flagset.Bool("clean", false, "Cleans seed files locally") - secretIDPtr := flagset.String("secretID", "", "Secret for app role ID") - appRoleIDPtr := flagset.String("appRoleID", "", "Public app role ID") - tokenNamePtr := flagset.String("tokenName", "", "Token name used by this "+coreopts.BuildOptions.GetFolderPrefix(nil)+"x to access the vault") - noVaultPtr := flagset.Bool("novault", false, "Don't pull configuration data from vault.") - pingPtr := flagset.Bool("ping", false, "Ping vault.") - - fileAddrPtr := flagset.String("seedpath", "", "Path for seed file") - fieldsPtr := flagset.String("fields", "", "Fields to enter") - encryptedPtr := flagset.String("encrypted", "", "Fields to encrypt") - readOnlyPtr := flagset.Bool("readonly", false, "Fields to encrypt") - dynamicPathPtr := flagset.String("dynamicPath", "", "Generate seeds for a dynamic path in vault.") - - var insecurePtr *bool - if insecurePtrIn == nil { - insecurePtr = flagset.Bool("insecure", false, "By default, every ssl connection this tool makes is verified secure. This option allows to tool to continue with server connections considered insecure.") - } else { - insecurePtr = insecurePtrIn - } - - diffPtr := flagset.Bool("diff", false, "Diff files") - versionPtr := flagset.Bool("versions", false, "Gets version metadata information") - wantCertsPtr := flagset.Bool("certs", false, "Pull certificates into directory specified by endDirPtr") - filterTemplatePtr := flagset.String("templateFilter", "", "Specifies which templates to filter") // -templateFilter=config.yml - - eUtils.CheckInitFlags(flagset) - - // Checks for proper flag input - args := argLines[1:] - for i := 0; i < len(args); i++ { - s := args[i] - if s[0] != '-' { - fmt.Println("Wrong flag syntax: ", s) - os.Exit(1) - } - } - - flagset.Parse(argLines[1:]) - configCtx := &eUtils.ConfigContext{ - ResultMap: make(map[string]*string), - EnvSlice: make([]string, 0), - ProjectSectionsSlice: make([]string, 0), - ResultChannel: make(chan *eUtils.ResultData, 5), - FileSysIndex: -1, - ConfigWg: sync.WaitGroup{}, - Mutex: &sync.Mutex{}, - } - - driverConfig := &eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - }, - Insecure: *insecurePtr, - } - - // Initialize logging - f, err := os.OpenFile(*logFilePtr, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) - if f != nil { - // Terminate logging - defer f.Close() - } - eUtils.CheckError(&driverConfig.CoreConfig, err, true) - logger := log.New(f, "["+coreopts.BuildOptions.GetFolderPrefix(nil)+"x]", log.LstdFlags) - driverConfig.CoreConfig.Log = logger - - envBasis := *envPtr - - Yellow := "\033[33m" - Reset := "\033[0m" - if eUtils.IsWindows() { - Reset = "" - Yellow = "" - } - - var fileFilter []string - if len(*filterTemplatePtr) != 0 { - fileFilter = strings.Split(*filterTemplatePtr, ",") - } - - //check for clean + env flag - cleanPresent := false - envPresent := false - for _, arg := range args { - if strings.Contains(arg, "clean") { - cleanPresent = true - } - if strings.Contains(arg, "env") { - envPresent = true - } - } - - if cleanPresent && !envPresent { - fmt.Println("Environment must be defined with -env=env1,... for -clean usage") - os.Exit(1) - } else if *diffPtr && *versionPtr { - fmt.Println("-version flag cannot be used with -diff flag") - os.Exit(1) - } else if *versionPtr && len(*eUtils.RestrictedPtr) > 0 { - fmt.Println("-restricted flags cannot be used with -versions flag") - os.Exit(1) - } else if (strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod")) && *addrPtr == "" { - fmt.Println("The -addr flag must be used with staging/prod environment") - os.Exit(1) - } else if (len(*fieldsPtr) == 0) && len(*fileAddrPtr) != 0 { - fmt.Println("The -fields flag must be used with -seedPath flag; -encrypted flag is optional") - os.Exit(1) - } else if *readOnlyPtr && (len(*encryptedPtr) == 0 || len(*fileAddrPtr) == 0) { - fmt.Println("The -encrypted flag must be used with -seedPath flag if -readonly is used") - os.Exit(1) - } else { - if len(*dynamicPathPtr) == 0 { - if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexNameFilterPtr) == 0) && len(*eUtils.IndexedPtr) != 0 { - fmt.Println("-serviceFilter and -indexFilter must be specified to use -indexed flag") - os.Exit(1) - } else if len(*eUtils.ServiceFilterPtr) == 0 && len(*eUtils.RestrictedPtr) != 0 { - fmt.Println("-serviceFilter must be specified to use -restricted flag") - os.Exit(1) - } else if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexValueFilterPtr) == 0) && *diffPtr && len(*eUtils.IndexedPtr) != 0 { - fmt.Println("-indexFilter and -indexValueFilter must be specified to use -indexed & -diff flag") - os.Exit(1) - } else if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexValueFilterPtr) == 0) && *versionPtr && len(*eUtils.IndexedPtr) != 0 { - fmt.Println("-indexFilter and -indexValueFilter must be specified to use -indexed & -versions flag") - os.Exit(1) - } - } - } - - trcxe := false - sectionSlice := []string{""} - if len(*fileAddrPtr) != 0 { //Checks if seed file exists & figured out if index/restricted - trcxe = true - directorySplit := strings.Split(*fileAddrPtr, "/") - indexed := false - if !*noVaultPtr { - pwd, _ := os.Getwd() - fileIndex, fileErr := os.Open(pwd + "/" + coreopts.BuildOptions.GetFolderPrefix(nil) + "_seeds/" + *envPtr + "/Index/" + *fileAddrPtr + "_seed.yml") - if fileIndex != nil { - defer fileIndex.Close() - } - if errors.Is(fileErr, os.ErrNotExist) { - fileRestricted, fileRErr := os.Open(pwd + "/" + coreopts.BuildOptions.GetFolderPrefix(nil) + "_seeds/" + *envPtr + "/Restricted/" + *fileAddrPtr + "_seed.yml") - if fileRestricted != nil { - defer fileRestricted.Close() - } - if errors.Is(fileRErr, os.ErrNotExist) { - fmt.Println("Specified seed file could not be found.") - os.Exit(1) - } - } else { - indexed = true - } - } else { - indexed = true - } - - if indexed { - if len(directorySplit) >= 3 { //Don't like this, will change later - *eUtils.IndexedPtr = directorySplit[0] - *eUtils.IndexNameFilterPtr = directorySplit[1] - *eUtils.IndexValueFilterPtr = directorySplit[2] - sectionSlice = strings.Split(*eUtils.IndexValueFilterPtr, ",") - } - } else { - fmt.Println("Not supported for restricted section.") - os.Exit(1) - } - } - - if len(*eUtils.ServiceFilterPtr) != 0 && len(*eUtils.IndexNameFilterPtr) == 0 && len(*eUtils.RestrictedPtr) != 0 { - eUtils.IndexNameFilterPtr = eUtils.ServiceFilterPtr - } - - keysCheck := make(map[string]bool) - listCheck := []string{} - - if *versionPtr { - if strings.Contains(*envPtr, ",") { - fmt.Println(Yellow + "Invalid environment, please specify one environment." + Reset) - os.Exit(1) - } - envVersion := strings.Split(*envPtr, "_") - if len(envVersion) > 1 && envVersion[1] != "" && envVersion[1] != "0" { - fmt.Println(Yellow + "Specified versioning not available, using " + envVersion[0] + " as environment" + Reset) - *envPtr = strings.Split(*envPtr, "_")[0] - } - configCtx.EnvSlice = append(configCtx.EnvSlice, *envPtr+"_versionInfo") - goto skipDiff - } - - //Diff flag parsing check - if *diffPtr { - if strings.ContainsAny(*envPtr, ",") { //Multiple environments - *envPtr = strings.ReplaceAll(*envPtr, "latest", "0") - configCtx.EnvSlice = strings.Split(*envPtr, ",") - configCtx.EnvLength = len(configCtx.EnvSlice) - if len(configCtx.EnvSlice) > 4 { - fmt.Println("Unsupported number of environments - Maximum: 4") - os.Exit(1) - } - for i, env := range configCtx.EnvSlice { - if env == "local" { - fmt.Println("Unsupported env: local not available with diff flag") - os.Exit(1) - } - if !strings.Contains(env, "_") { - configCtx.EnvSlice[i] = env + "_0" - } - } - } else { - fmt.Println("Incorrect format for diff: -env=env1,env2,...") - os.Exit(1) - } - } else { - if strings.ContainsAny(*envPtr, ",") { - fmt.Println("-diff flag is required for multiple environments - env: -env=env1,env2,...") - os.Exit(1) - } - configCtx.EnvSlice = append(configCtx.EnvSlice, (*envPtr)) - envVersion := strings.Split(*envPtr, "_") //Break apart env+version for token - *envPtr = envVersion[0] - if !*noVaultPtr { - autoErr := eUtils.AutoAuth(driverConfig, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - - if autoErr != nil { - fmt.Println("Auth failure: " + autoErr.Error()) - eUtils.LogErrorMessage(&driverConfig.CoreConfig, autoErr.Error(), true) - } - } else { - *tokenPtr = "novault" - } - - if len(envVersion) >= 2 { //Put back env+version together - *envPtr = envVersion[0] + "_" + envVersion[1] - if envVersion[1] == "" { - fmt.Println("Must declare desired version number after '_' : -env=env1_ver1") - os.Exit(1) - } - } else { - *envPtr = envVersion[0] + "_0" - } - } - - if memonly.IsMemonly() { - memprotectopts.MemUnprotectAll(nil) - memprotectopts.MemProtect(nil, tokenPtr) - } - - //Duplicate env check - for _, entry := range configCtx.EnvSlice { - if _, value := keysCheck[entry]; !value { - keysCheck[entry] = true - listCheck = append(listCheck, entry) - } - } - - if len(listCheck) != len(configCtx.EnvSlice) { - fmt.Printf("Cannot diff an environment against itself.\n") - os.Exit(1) - } - -skipDiff: - // Prints usage if no flags are specified - if *helpPtr { - flagset.Usage() - os.Exit(1) - } - if ctx == nil { - if _, err := os.Stat(*startDirPtr); os.IsNotExist(err) { - fmt.Println("Missing required start template folder: " + *startDirPtr) - os.Exit(1) - } - if !*diffPtr { // -diff doesn't require seed folder - if _, err := os.Stat(*endDirPtr); os.IsNotExist(err) { - fmt.Println("Missing required start seed folder: " + *endDirPtr) - os.Exit(1) - } - } - } - - // If logging production directory does not exist and is selected log to local directory - if _, err := os.Stat("/var/log/"); os.IsNotExist(err) && *logFilePtr == "/var/log/"+coreopts.BuildOptions.GetFolderPrefix(nil)+"x.log" { - *logFilePtr = "./" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x.log" - } - - regions := []string{} - - if len(configCtx.EnvSlice) == 1 && !*noVaultPtr { - if strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod") { - secretIDPtr = nil - appRoleIDPtr = nil - } - if strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod") || strings.HasPrefix(*envPtr, "dev") { - regions = eUtils.GetSupportedProdRegions() - } - autoErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr, - }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - if autoErr != nil { - fmt.Println("Missing auth components.") - eUtils.LogErrorMessage(&driverConfig.CoreConfig, autoErr.Error(), true) - } - } - - if (tokenPtr == nil || *tokenPtr == "") && !*noVaultPtr && len(configCtx.EnvSlice) == 1 { - fmt.Println("Missing required auth token.") - os.Exit(1) - } - - if len(*envPtr) >= 5 && (*envPtr)[:5] == "local" { - var err error - *envPtr, err = eUtils.LoginToLocal() - fmt.Println(*envPtr) - eUtils.CheckError(&driverConfig.CoreConfig, err, true) - } - - logger.Println("=============== Initializing Seed Generator ===============") - - logger.SetPrefix("[" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x]") - logger.Printf("Looking for template(s) in directory: %s\n", *startDirPtr) - - var subSectionName string - if len(*eUtils.IndexNameFilterPtr) > 0 { - subSectionName = *eUtils.IndexNameFilterPtr - } else { - subSectionName = "" - } - var waitg sync.WaitGroup - sectionKey := "" - var serviceFilterSlice []string - - if len(*dynamicPathPtr) > 0 { - go receiver(configCtx) //Channel receiver - - dynamicPathParts := strings.Split(*dynamicPathPtr, "/") - - for _, env := range configCtx.EnvSlice { - envVersion := eUtils.SplitEnv(env) - *envPtr = envVersion[0] - if secretIDPtr != nil && *secretIDPtr != "" && appRoleIDPtr != nil && *appRoleIDPtr != "" { - *tokenPtr = "" - } - var testMod *kv.Modifier = nil - var baseEnv string - - if strings.Contains(*dynamicPathPtr, "%s") { - if strings.Contains(configCtx.EnvSlice[0], "_") { - baseEnv = strings.Split(configCtx.EnvSlice[0], "_")[0] - } else { - baseEnv = configCtx.EnvSlice[0] - } - if !*noVaultPtr && *tokenPtr == "" { - //Ask vault for list of dev..* environments, add to envSlice - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr, - }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, &baseEnv, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Auth failure: "+authErr.Error(), true) - } - } - } - - // Look up and flush out any dynamic components. - pathGen := "" - var recursivePathBuilder func(testMod *kv.Modifier, pGen string, dynamicPathParts []string) - - recursivePathBuilder = func(testMod *kv.Modifier, pGen string, dynamicPathParts []string) { - if len(dynamicPathParts) == 0 { - if !*noVaultPtr && *tokenPtr == "" { - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr, - }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - // Retry once. - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr, - }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - eUtils.LogAndSafeExit(&driverConfig.CoreConfig, fmt.Sprintf("Unexpected auth error %v ", authErr), 1) - } - } - } else if *tokenPtr == "" { - *tokenPtr = "novault" - } - if len(envVersion) >= 2 { //Put back env+version together - *envPtr = envVersion[0] + "_" + envVersion[1] - } else { - *envPtr = envVersion[0] + "_0" - } - - driverConfig := &eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - WantCerts: *wantCertsPtr, - DynamicPathFilter: pGen, - ExitOnFailure: true, - Log: logger, - }, - Context: ctx, - Insecure: *insecurePtr, - Token: *tokenPtr, - VaultAddress: *addrPtr, - EnvBasis: envBasis, - Env: *envPtr, - Regions: regions, - SecretMode: *secretMode, - StartDir: append([]string{}, *startDirPtr), - EndDir: *endDirPtr, - GenAuth: *genAuth, - Clean: *cleanPtr, - Diff: *diffPtr, - Update: messenger, - VersionInfo: eUtils.VersionHelper, - SubPathFilter: strings.Split(pGen, ","), - FileFilter: fileFilter, - Trcxr: *readOnlyPtr, - } - waitg.Add(1) - go func(dc *eUtils.DriverConfig) { - defer waitg.Done() - eUtils.ConfigControl(ctx, configCtx, dc, configDriver) - }(driverConfig) - return - } - - for i, dynamicPart := range dynamicPathParts { - if dynamicPart == "%s" { - if testMod == nil { - testMod, err = kv.NewModifier(*insecurePtr, *tokenPtr, *addrPtr, baseEnv, regions, true, logger) - testMod.Env = baseEnv - if err != nil { - eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Access to vault failure.", true) - } - } - - listValues, err := testMod.ListEnv("super-secrets/"+testMod.Env+"/"+pGen, driverConfig.CoreConfig.Log) - if err != nil { - if strings.Contains(err.Error(), "permission denied") { - eUtils.LogErrorMessage(&driverConfig.CoreConfig, fmt.Sprintf("Insufficient privileges accessing: %s", pGen), true) - } - } - - if listValues == nil { - // eUtils.LogInfo(config, fmt.Sprintf("Partial data with path: %s", "super-secrets/"+testMod.Env+"/"+pGen)) - return - } - levelPart := map[string]string{} - for _, valuesPath := range listValues.Data { - for _, indexNameInterface := range valuesPath.([]interface{}) { - levelPart[strings.Trim(indexNameInterface.(string), "/")] = "" - } - } - - if len(dynamicPathParts) > i { - for level := range levelPart { - recursivePathBuilder(testMod, pGen+"/"+level, dynamicPathParts[i+1:]) - } - return - } - } else { - if len(pGen) > 0 { - pGen = pGen + "/" + dynamicPart - } else { - pGen = pGen + dynamicPart - } - } - } - recursivePathBuilder(testMod, pGen, []string{}) - } - - recursivePathBuilder(testMod, pathGen, dynamicPathParts) - - if testMod != nil { - testMod.Release() - } - } - - } else { - sectionKey = "/" - - // TODO: Deprecated... - // 1-800-ROIT - if len(configCtx.EnvSlice) == 1 || (len(*eUtils.IndexValueFilterPtr) > 0 && len(*eUtils.IndexedPtr) > 0) { - if strings.Contains(configCtx.EnvSlice[0], "*") || len(*eUtils.IndexedPtr) > 0 || len(*eUtils.RestrictedPtr) > 0 || len(*eUtils.ProtectedPtr) > 0 { - if len(*eUtils.IndexedPtr) > 0 { - sectionKey = "/Index/" - } else if len(*eUtils.RestrictedPtr) > 0 { - sectionKey = "/Restricted/" - } else if len(*eUtils.ProtectedPtr) > 0 { - sectionKey = "/Protected/" - } - - newSectionSlice := make([]string, 0) - if !*noVaultPtr && !trcxe { - var baseEnv string - if strings.Contains(configCtx.EnvSlice[0], "_") { - baseEnv = strings.Split(configCtx.EnvSlice[0], "_")[0] - } else { - baseEnv = configCtx.EnvSlice[0] - } - //Ask vault for list of dev..* environments, add to envSlice - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr}, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, &baseEnv, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Auth failure: "+authErr.Error(), true) - } - testMod, err := kv.NewModifier(*insecurePtr, *tokenPtr, *addrPtr, baseEnv, regions, true, logger) - testMod.Env = baseEnv - if err != nil { - logger.Printf(err.Error()) - } - // Only look at index values.... - //Checks for indexed projects - if len(*eUtils.IndexedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) - } - - if len(*eUtils.RestrictedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.RestrictedPtr, ",")...) - } - - if len(*eUtils.ProtectedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.ProtectedPtr, ",")...) - } - - var listValues *api.Secret - if len(configCtx.ProjectSectionsSlice) > 0 { //If eid -> look inside Index and grab all environments - subSectionPath := configCtx.ProjectSectionsSlice[0] + "/" - listValues, err = testMod.ListEnv("super-secrets/"+testMod.Env+sectionKey+subSectionPath, driverConfig.CoreConfig.Log) - if err != nil { - if strings.Contains(err.Error(), "permission denied") { - eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Attempt to access restricted section of the vault denied.", true) - } - } - - // Further path modifications needed. - if listValues == nil { - eUtils.LogAndSafeExit(&driverConfig.CoreConfig, "No available indexes found for "+subSectionPath, 1) - } - for k, valuesPath := range listValues.Data { - for _, indexNameInterface := range valuesPath.([]interface{}) { - if indexNameInterface != (subSectionName + "/") { - continue - } - indexList, err := testMod.ListEnv("super-secrets/"+testMod.Env+sectionKey+subSectionPath+"/"+indexNameInterface.(string), driverConfig.CoreConfig.Log) - if err != nil { - logger.Printf(err.Error()) - } - - for _, indexPath := range indexList.Data { - for _, indexInterface := range indexPath.([]interface{}) { - if len(*eUtils.IndexValueFilterPtr) > 0 { - if indexInterface != (*eUtils.IndexValueFilterPtr + "/") { - continue - } - } - newSectionSlice = append(newSectionSlice, strings.ReplaceAll(indexInterface.(string), "/", "")) - } - } - } - delete(listValues.Data, k) //delete it so it doesn't repeat below - } - } else { - listValues, err = testMod.ListEnv("values/", driverConfig.CoreConfig.Log) - } - if err != nil { - logger.Printf(err.Error()) - } - if len(newSectionSlice) > 0 { - sectionSlice = newSectionSlice - } - if testMod != nil { - testMod.Release() - } - } else { //novault takes this path - if len(*eUtils.IndexedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) - } - - if len(*eUtils.RestrictedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.RestrictedPtr, ",")...) - } - - if len(*eUtils.ProtectedPtr) > 0 { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.ProtectedPtr, ",")...) - } - } - } - } - - var filteredSectionSlice []string - - if len(*eUtils.IndexValueFilterPtr) > 0 { - filterSlice := strings.Split(*eUtils.IndexValueFilterPtr, ",") - for _, filter := range filterSlice { - for _, section := range sectionSlice { - if filter == section { - filteredSectionSlice = append(filteredSectionSlice, section) - } - } - } - sectionSlice = filteredSectionSlice - } - if len(*eUtils.ServiceFilterPtr) > 0 { - if len(sectionSlice) == 0 { - eUtils.LogAndSafeExit(&driverConfig.CoreConfig, "No available indexes found for "+*eUtils.IndexValueFilterPtr, 1) - } - serviceFilterSlice = strings.Split(*eUtils.ServiceFilterPtr, ",") - if len(*eUtils.ServiceNameFilterPtr) > 0 { - *eUtils.ServiceNameFilterPtr = "/" + *eUtils.ServiceNameFilterPtr //added "/" - used path later - } - } - } - - go receiver(configCtx) //Channel receiver - if len(*dynamicPathPtr) == 0 { - for _, env := range configCtx.EnvSlice { - envVersion := eUtils.SplitEnv(env) - *envPtr = envVersion[0] - if secretIDPtr != nil && *secretIDPtr != "" && appRoleIDPtr != nil && *appRoleIDPtr != "" { - *tokenPtr = "" - } - for _, section := range sectionSlice { - var servicesWanted []string - if !*noVaultPtr && *tokenPtr == "" { - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr}, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - // Retry once. - authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - Log: logger, - }, - Insecure: *insecurePtr, - }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) - if authErr != nil { - eUtils.LogAndSafeExit(&driverConfig.CoreConfig, fmt.Sprintf("Unexpected auth error %v ", authErr), 1) - } - } - } else if *tokenPtr == "" { - *tokenPtr = "novault" - } - if len(envVersion) >= 2 { //Put back env+version together - *envPtr = envVersion[0] + "_" + envVersion[1] - } else { - *envPtr = envVersion[0] + "_0" - } - - var trcxeList []string - if trcxe { - configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) - - trcxeList = append(trcxeList, *fieldsPtr) - trcxeList = append(trcxeList, *encryptedPtr) - if *noVaultPtr { - trcxeList = append(trcxeList, "new") - } - } - driverConfig := &eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - WantCerts: *wantCertsPtr, - DynamicPathFilter: *dynamicPathPtr, - ExitOnFailure: true, - Log: logger, - }, - Context: ctx, - Insecure: *insecurePtr, - Token: *tokenPtr, - VaultAddress: *addrPtr, - EnvBasis: envBasis, - Env: *envPtr, - SectionKey: sectionKey, - SectionName: subSectionName, - SubSectionValue: section, - SubSectionName: *eUtils.ServiceNameFilterPtr, - Regions: regions, - SecretMode: *secretMode, - ServicesWanted: servicesWanted, - StartDir: append([]string{}, *startDirPtr), - EndDir: *endDirPtr, - GenAuth: *genAuth, - Clean: *cleanPtr, - Diff: *diffPtr, - Update: messenger, - VersionInfo: eUtils.VersionHelper, - FileFilter: fileFilter, - SubPathFilter: strings.Split(*eUtils.SubPathFilter, ","), - ProjectSections: configCtx.ProjectSectionsSlice, - ServiceFilter: serviceFilterSlice, - Trcxe: trcxeList, - Trcxr: *readOnlyPtr, - } - waitg.Add(1) - go func(dc *eUtils.DriverConfig) { - defer waitg.Done() - eUtils.ConfigControl(ctx, configCtx, dc, configDriver) - }(driverConfig) - } - } - } - - waitg.Wait() - close(configCtx.ResultChannel) - if *diffPtr { //Diff if needed - waitg.Add(1) - go func(cctx *eUtils.ConfigContext) { - defer waitg.Done() - retry := 0 - for { - cctx.Mutex.Lock() - if len(cctx.ResultMap) == len(cctx.EnvSlice)*len(sectionSlice) || retry == 3 { - cctx.Mutex.Unlock() - break - } - cctx.Mutex.Unlock() - time.Sleep(time.Duration(time.Second)) - retry++ - } - configCtx.FileSysIndex = -1 - cctx.SetDiffFileCount(len(configCtx.ResultMap) / configCtx.EnvLength) - eUtils.DiffHelper(cctx, false) - }(configCtx) - } - waitg.Wait() //Wait for diff - - logger.SetPrefix("[" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x]") - logger.Println("=============== Terminating Seed Generator ===============") - logger.SetPrefix("[END]") - logger.Println() -} +package trcxbase + +import ( + "errors" + "flag" + "fmt" + "log" + "os" + "strings" + "sync" + "time" + + "github.com/trimble-oss/tierceron/buildopts/coreopts" + "github.com/trimble-oss/tierceron/buildopts/memonly" + "github.com/trimble-oss/tierceron/buildopts/memprotectopts" + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + + "github.com/hashicorp/vault/api" +) + +func messenger(configCtx *eUtils.ConfigContext, inData *string, inPath string) { + var data eUtils.ResultData + data.InData = inData + data.InPath = inPath + configCtx.ResultChannel <- &data +} + +func receiver(configCtx *eUtils.ConfigContext) { + for { + select { + case data := <-configCtx.ResultChannel: + if data != nil && data.InData != nil && data.InPath != "" { + configCtx.Mutex.Lock() + configCtx.ResultMap[data.InPath] = data.InData + configCtx.Mutex.Unlock() + } + } + } +} + +// CommonMain This executable automates the creation of seed files from template file(s). +// New seed files are written (or overwrite current seed files) to the specified directory. +func CommonMain(ctx eUtils.ProcessContext, + configDriver eUtils.ConfigDriver, + envPtr *string, + addrPtrIn *string, + envCtxPtr *string, + insecurePtrIn *bool, + flagset *flag.FlagSet, + argLines []string) { + // Executable input arguments(flags) + if flagset == nil { + flagset = flag.NewFlagSet(argLines[0], flag.ExitOnError) + flagset.Usage = func() { + fmt.Fprintf(flagset.Output(), "Usage of %s:\n", argLines[0]) + flagset.PrintDefaults() + } + flagset.String("env", "dev", "Environment to configure") + } + addrPtr := flagset.String("addr", "", "API endpoint for the vault") + if addrPtrIn != nil && *addrPtrIn != "" { + addrPtr = addrPtrIn + } + + startDirPtr := flagset.String("startDir", coreopts.BuildOptions.GetFolderPrefix(nil)+"_templates", "Pull templates from this directory") + endDirPtr := flagset.String("endDir", "./"+coreopts.BuildOptions.GetFolderPrefix(nil)+"_seeds/", "Write generated seed files to this directory") + logFilePtr := flagset.String("log", "./"+coreopts.BuildOptions.GetFolderPrefix(nil)+"x.log", "Output path for log file") + helpPtr := flagset.Bool("h", false, "Provide options for "+coreopts.BuildOptions.GetFolderPrefix(nil)+"x") + tokenPtr := flagset.String("token", "", "Vault access token") + secretMode := flagset.Bool("secretMode", true, "Only override secret values in templates?") + genAuth := flagset.Bool("genAuth", false, "Generate auth section of seed data?") + cleanPtr := flagset.Bool("clean", false, "Cleans seed files locally") + secretIDPtr := flagset.String("secretID", "", "Secret for app role ID") + appRoleIDPtr := flagset.String("appRoleID", "", "Public app role ID") + tokenNamePtr := flagset.String("tokenName", "", "Token name used by this "+coreopts.BuildOptions.GetFolderPrefix(nil)+"x to access the vault") + noVaultPtr := flagset.Bool("novault", false, "Don't pull configuration data from vault.") + pingPtr := flagset.Bool("ping", false, "Ping vault.") + + fileAddrPtr := flagset.String("seedpath", "", "Path for seed file") + fieldsPtr := flagset.String("fields", "", "Fields to enter") + encryptedPtr := flagset.String("encrypted", "", "Fields to encrypt") + readOnlyPtr := flagset.Bool("readonly", false, "Fields to encrypt") + dynamicPathPtr := flagset.String("dynamicPath", "", "Generate seeds for a dynamic path in vault.") + + var insecurePtr *bool + if insecurePtrIn == nil { + insecurePtr = flagset.Bool("insecure", false, "By default, every ssl connection this tool makes is verified secure. This option allows to tool to continue with server connections considered insecure.") + } else { + insecurePtr = insecurePtrIn + } + + diffPtr := flagset.Bool("diff", false, "Diff files") + versionPtr := flagset.Bool("versions", false, "Gets version metadata information") + wantCertsPtr := flagset.Bool("certs", false, "Pull certificates into directory specified by endDirPtr") + filterTemplatePtr := flagset.String("templateFilter", "", "Specifies which templates to filter") // -templateFilter=config.yml + + eUtils.CheckInitFlags(flagset) + + // Checks for proper flag input + args := argLines[1:] + for i := 0; i < len(args); i++ { + s := args[i] + if s[0] != '-' { + fmt.Println("Wrong flag syntax: ", s) + os.Exit(1) + } + } + + flagset.Parse(argLines[1:]) + configCtx := &eUtils.ConfigContext{ + ResultMap: make(map[string]*string), + EnvSlice: make([]string, 0), + ProjectSectionsSlice: make([]string, 0), + ResultChannel: make(chan *eUtils.ResultData, 5), + FileSysIndex: -1, + ConfigWg: sync.WaitGroup{}, + Mutex: &sync.Mutex{}, + } + + driverConfig := &eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + }, + Insecure: *insecurePtr, + } + + // Initialize logging + f, err := os.OpenFile(*logFilePtr, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) + if f != nil { + // Terminate logging + defer f.Close() + } + eUtils.CheckError(&driverConfig.CoreConfig, err, true) + logger := log.New(f, "["+coreopts.BuildOptions.GetFolderPrefix(nil)+"x]", log.LstdFlags) + driverConfig.CoreConfig.Log = logger + + envBasis := *envPtr + + Yellow := "\033[33m" + Reset := "\033[0m" + if eUtils.IsWindows() { + Reset = "" + Yellow = "" + } + + var fileFilter []string + if len(*filterTemplatePtr) != 0 { + fileFilter = strings.Split(*filterTemplatePtr, ",") + } + + //check for clean + env flag + cleanPresent := false + envPresent := false + for _, arg := range args { + if strings.Contains(arg, "clean") { + cleanPresent = true + } + if strings.Contains(arg, "env") { + envPresent = true + } + } + + if cleanPresent && !envPresent { + fmt.Println("Environment must be defined with -env=env1,... for -clean usage") + os.Exit(1) + } else if *diffPtr && *versionPtr { + fmt.Println("-version flag cannot be used with -diff flag") + os.Exit(1) + } else if *versionPtr && len(*eUtils.RestrictedPtr) > 0 { + fmt.Println("-restricted flags cannot be used with -versions flag") + os.Exit(1) + } else if (strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod")) && *addrPtr == "" { + fmt.Println("The -addr flag must be used with staging/prod environment") + os.Exit(1) + } else if (len(*fieldsPtr) == 0) && len(*fileAddrPtr) != 0 { + fmt.Println("The -fields flag must be used with -seedPath flag; -encrypted flag is optional") + os.Exit(1) + } else if *readOnlyPtr && (len(*encryptedPtr) == 0 || len(*fileAddrPtr) == 0) { + fmt.Println("The -encrypted flag must be used with -seedPath flag if -readonly is used") + os.Exit(1) + } else { + if len(*dynamicPathPtr) == 0 { + if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexNameFilterPtr) == 0) && len(*eUtils.IndexedPtr) != 0 { + fmt.Println("-serviceFilter and -indexFilter must be specified to use -indexed flag") + os.Exit(1) + } else if len(*eUtils.ServiceFilterPtr) == 0 && len(*eUtils.RestrictedPtr) != 0 { + fmt.Println("-serviceFilter must be specified to use -restricted flag") + os.Exit(1) + } else if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexValueFilterPtr) == 0) && *diffPtr && len(*eUtils.IndexedPtr) != 0 { + fmt.Println("-indexFilter and -indexValueFilter must be specified to use -indexed & -diff flag") + os.Exit(1) + } else if (len(*eUtils.ServiceFilterPtr) == 0 || len(*eUtils.IndexValueFilterPtr) == 0) && *versionPtr && len(*eUtils.IndexedPtr) != 0 { + fmt.Println("-indexFilter and -indexValueFilter must be specified to use -indexed & -versions flag") + os.Exit(1) + } + } + } + + trcxe := false + sectionSlice := []string{""} + if len(*fileAddrPtr) != 0 { //Checks if seed file exists & figured out if index/restricted + trcxe = true + directorySplit := strings.Split(*fileAddrPtr, "/") + indexed := false + if !*noVaultPtr { + pwd, _ := os.Getwd() + fileIndex, fileErr := os.Open(pwd + "/" + coreopts.BuildOptions.GetFolderPrefix(nil) + "_seeds/" + *envPtr + "/Index/" + *fileAddrPtr + "_seed.yml") + if fileIndex != nil { + defer fileIndex.Close() + } + if errors.Is(fileErr, os.ErrNotExist) { + fileRestricted, fileRErr := os.Open(pwd + "/" + coreopts.BuildOptions.GetFolderPrefix(nil) + "_seeds/" + *envPtr + "/Restricted/" + *fileAddrPtr + "_seed.yml") + if fileRestricted != nil { + defer fileRestricted.Close() + } + if errors.Is(fileRErr, os.ErrNotExist) { + fmt.Println("Specified seed file could not be found.") + os.Exit(1) + } + } else { + indexed = true + } + } else { + indexed = true + } + + if indexed { + if len(directorySplit) >= 3 { //Don't like this, will change later + *eUtils.IndexedPtr = directorySplit[0] + *eUtils.IndexNameFilterPtr = directorySplit[1] + *eUtils.IndexValueFilterPtr = directorySplit[2] + sectionSlice = strings.Split(*eUtils.IndexValueFilterPtr, ",") + } + } else { + fmt.Println("Not supported for restricted section.") + os.Exit(1) + } + } + + if len(*eUtils.ServiceFilterPtr) != 0 && len(*eUtils.IndexNameFilterPtr) == 0 && len(*eUtils.RestrictedPtr) != 0 { + eUtils.IndexNameFilterPtr = eUtils.ServiceFilterPtr + } + + keysCheck := make(map[string]bool) + listCheck := []string{} + + if *versionPtr { + if strings.Contains(*envPtr, ",") { + fmt.Println(Yellow + "Invalid environment, please specify one environment." + Reset) + os.Exit(1) + } + envVersion := strings.Split(*envPtr, "_") + if len(envVersion) > 1 && envVersion[1] != "" && envVersion[1] != "0" { + fmt.Println(Yellow + "Specified versioning not available, using " + envVersion[0] + " as environment" + Reset) + *envPtr = strings.Split(*envPtr, "_")[0] + } + configCtx.EnvSlice = append(configCtx.EnvSlice, *envPtr+"_versionInfo") + goto skipDiff + } + + //Diff flag parsing check + if *diffPtr { + if strings.ContainsAny(*envPtr, ",") { //Multiple environments + *envPtr = strings.ReplaceAll(*envPtr, "latest", "0") + configCtx.EnvSlice = strings.Split(*envPtr, ",") + configCtx.EnvLength = len(configCtx.EnvSlice) + if len(configCtx.EnvSlice) > 4 { + fmt.Println("Unsupported number of environments - Maximum: 4") + os.Exit(1) + } + for i, env := range configCtx.EnvSlice { + if env == "local" { + fmt.Println("Unsupported env: local not available with diff flag") + os.Exit(1) + } + if !strings.Contains(env, "_") { + configCtx.EnvSlice[i] = env + "_0" + } + } + } else { + fmt.Println("Incorrect format for diff: -env=env1,env2,...") + os.Exit(1) + } + } else { + if strings.ContainsAny(*envPtr, ",") { + fmt.Println("-diff flag is required for multiple environments - env: -env=env1,env2,...") + os.Exit(1) + } + configCtx.EnvSlice = append(configCtx.EnvSlice, (*envPtr)) + envVersion := strings.Split(*envPtr, "_") //Break apart env+version for token + *envPtr = envVersion[0] + if !*noVaultPtr { + autoErr := eUtils.AutoAuth(driverConfig, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + + if autoErr != nil { + fmt.Println("Auth failure: " + autoErr.Error()) + eUtils.LogErrorMessage(&driverConfig.CoreConfig, autoErr.Error(), true) + } + } else { + *tokenPtr = "novault" + } + + if len(envVersion) >= 2 { //Put back env+version together + *envPtr = envVersion[0] + "_" + envVersion[1] + if envVersion[1] == "" { + fmt.Println("Must declare desired version number after '_' : -env=env1_ver1") + os.Exit(1) + } + } else { + *envPtr = envVersion[0] + "_0" + } + } + + if memonly.IsMemonly() { + memprotectopts.MemUnprotectAll(nil) + memprotectopts.MemProtect(nil, tokenPtr) + } + + //Duplicate env check + for _, entry := range configCtx.EnvSlice { + if _, value := keysCheck[entry]; !value { + keysCheck[entry] = true + listCheck = append(listCheck, entry) + } + } + + if len(listCheck) != len(configCtx.EnvSlice) { + fmt.Printf("Cannot diff an environment against itself.\n") + os.Exit(1) + } + +skipDiff: + // Prints usage if no flags are specified + if *helpPtr { + flagset.Usage() + os.Exit(1) + } + if ctx == nil { + if _, err := os.Stat(*startDirPtr); os.IsNotExist(err) { + fmt.Println("Missing required start template folder: " + *startDirPtr) + os.Exit(1) + } + if !*diffPtr { // -diff doesn't require seed folder + if _, err := os.Stat(*endDirPtr); os.IsNotExist(err) { + fmt.Println("Missing required start seed folder: " + *endDirPtr) + os.Exit(1) + } + } + } + + // If logging production directory does not exist and is selected log to local directory + if _, err := os.Stat("/var/log/"); os.IsNotExist(err) && *logFilePtr == "/var/log/"+coreopts.BuildOptions.GetFolderPrefix(nil)+"x.log" { + *logFilePtr = "./" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x.log" + } + + regions := []string{} + + if len(configCtx.EnvSlice) == 1 && !*noVaultPtr { + if strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod") { + secretIDPtr = nil + appRoleIDPtr = nil + } + if strings.HasPrefix(*envPtr, "staging") || strings.HasPrefix(*envPtr, "prod") || strings.HasPrefix(*envPtr, "dev") { + regions = eUtils.GetSupportedProdRegions() + } + autoErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr, + }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + if autoErr != nil { + fmt.Println("Missing auth components.") + eUtils.LogErrorMessage(&driverConfig.CoreConfig, autoErr.Error(), true) + } + } + + if (tokenPtr == nil || *tokenPtr == "") && !*noVaultPtr && len(configCtx.EnvSlice) == 1 { + fmt.Println("Missing required auth token.") + os.Exit(1) + } + + if len(*envPtr) >= 5 && (*envPtr)[:5] == "local" { + var err error + *envPtr, err = eUtils.LoginToLocal() + fmt.Println(*envPtr) + eUtils.CheckError(&driverConfig.CoreConfig, err, true) + } + + logger.Println("=============== Initializing Seed Generator ===============") + + logger.SetPrefix("[" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x]") + logger.Printf("Looking for template(s) in directory: %s\n", *startDirPtr) + + var subSectionName string + if len(*eUtils.IndexNameFilterPtr) > 0 { + subSectionName = *eUtils.IndexNameFilterPtr + } else { + subSectionName = "" + } + var waitg sync.WaitGroup + sectionKey := "" + var serviceFilterSlice []string + + if len(*dynamicPathPtr) > 0 { + go receiver(configCtx) //Channel receiver + + dynamicPathParts := strings.Split(*dynamicPathPtr, "/") + + for _, env := range configCtx.EnvSlice { + envVersion := eUtils.SplitEnv(env) + *envPtr = envVersion[0] + if secretIDPtr != nil && *secretIDPtr != "" && appRoleIDPtr != nil && *appRoleIDPtr != "" { + *tokenPtr = "" + } + var testMod *kv.Modifier = nil + var baseEnv string + + if strings.Contains(*dynamicPathPtr, "%s") { + if strings.Contains(configCtx.EnvSlice[0], "_") { + baseEnv = strings.Split(configCtx.EnvSlice[0], "_")[0] + } else { + baseEnv = configCtx.EnvSlice[0] + } + if !*noVaultPtr && *tokenPtr == "" { + //Ask vault for list of dev..* environments, add to envSlice + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr, + }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, &baseEnv, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Auth failure: "+authErr.Error(), true) + } + } + } + + // Look up and flush out any dynamic components. + pathGen := "" + var recursivePathBuilder func(testMod *kv.Modifier, pGen string, dynamicPathParts []string) + + recursivePathBuilder = func(testMod *kv.Modifier, pGen string, dynamicPathParts []string) { + if len(dynamicPathParts) == 0 { + if !*noVaultPtr && *tokenPtr == "" { + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr, + }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + // Retry once. + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr, + }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + eUtils.LogAndSafeExit(&driverConfig.CoreConfig, fmt.Sprintf("Unexpected auth error %v ", authErr), 1) + } + } + } else if *tokenPtr == "" { + *tokenPtr = "novault" + } + if len(envVersion) >= 2 { //Put back env+version together + *envPtr = envVersion[0] + "_" + envVersion[1] + } else { + *envPtr = envVersion[0] + "_0" + } + + driverConfig := &eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + WantCerts: *wantCertsPtr, + DynamicPathFilter: pGen, + ExitOnFailure: true, + Log: logger, + }, + Context: ctx, + Insecure: *insecurePtr, + Token: *tokenPtr, + VaultAddress: *addrPtr, + EnvBasis: envBasis, + Env: *envPtr, + Regions: regions, + SecretMode: *secretMode, + StartDir: append([]string{}, *startDirPtr), + EndDir: *endDirPtr, + GenAuth: *genAuth, + Clean: *cleanPtr, + Diff: *diffPtr, + Update: messenger, + VersionInfo: eUtils.VersionHelper, + SubPathFilter: strings.Split(pGen, ","), + FileFilter: fileFilter, + Trcxr: *readOnlyPtr, + } + waitg.Add(1) + go func(dc *eUtils.DriverConfig) { + defer waitg.Done() + eUtils.ConfigControl(ctx, configCtx, dc, configDriver) + }(driverConfig) + return + } + + for i, dynamicPart := range dynamicPathParts { + if dynamicPart == "%s" { + if testMod == nil { + testMod, err = kv.NewModifier(*insecurePtr, *tokenPtr, *addrPtr, baseEnv, regions, true, logger) + testMod.Env = baseEnv + if err != nil { + eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Access to vault failure.", true) + } + } + + listValues, err := testMod.ListEnv("super-secrets/"+testMod.Env+"/"+pGen, driverConfig.CoreConfig.Log) + if err != nil { + if strings.Contains(err.Error(), "permission denied") { + eUtils.LogErrorMessage(&driverConfig.CoreConfig, fmt.Sprintf("Insufficient privileges accessing: %s", pGen), true) + } + } + + if listValues == nil { + // eUtils.LogInfo(config, fmt.Sprintf("Partial data with path: %s", "super-secrets/"+testMod.Env+"/"+pGen)) + return + } + levelPart := map[string]string{} + for _, valuesPath := range listValues.Data { + for _, indexNameInterface := range valuesPath.([]interface{}) { + levelPart[strings.Trim(indexNameInterface.(string), "/")] = "" + } + } + + if len(dynamicPathParts) > i { + for level := range levelPart { + recursivePathBuilder(testMod, pGen+"/"+level, dynamicPathParts[i+1:]) + } + return + } + } else { + if len(pGen) > 0 { + pGen = pGen + "/" + dynamicPart + } else { + pGen = pGen + dynamicPart + } + } + } + recursivePathBuilder(testMod, pGen, []string{}) + } + + recursivePathBuilder(testMod, pathGen, dynamicPathParts) + + if testMod != nil { + testMod.Release() + } + } + + } else { + sectionKey = "/" + + // TODO: Deprecated... + // 1-800-ROIT + if len(configCtx.EnvSlice) == 1 || (len(*eUtils.IndexValueFilterPtr) > 0 && len(*eUtils.IndexedPtr) > 0) { + if strings.Contains(configCtx.EnvSlice[0], "*") || len(*eUtils.IndexedPtr) > 0 || len(*eUtils.RestrictedPtr) > 0 || len(*eUtils.ProtectedPtr) > 0 { + if len(*eUtils.IndexedPtr) > 0 { + sectionKey = "/Index/" + } else if len(*eUtils.RestrictedPtr) > 0 { + sectionKey = "/Restricted/" + } else if len(*eUtils.ProtectedPtr) > 0 { + sectionKey = "/Protected/" + } + + newSectionSlice := make([]string, 0) + if !*noVaultPtr && !trcxe { + var baseEnv string + if strings.Contains(configCtx.EnvSlice[0], "_") { + baseEnv = strings.Split(configCtx.EnvSlice[0], "_")[0] + } else { + baseEnv = configCtx.EnvSlice[0] + } + //Ask vault for list of dev..* environments, add to envSlice + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr}, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, &baseEnv, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Auth failure: "+authErr.Error(), true) + } + testMod, err := kv.NewModifier(*insecurePtr, *tokenPtr, *addrPtr, baseEnv, regions, true, logger) + testMod.Env = baseEnv + if err != nil { + logger.Printf(err.Error()) + } + // Only look at index values.... + //Checks for indexed projects + if len(*eUtils.IndexedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) + } + + if len(*eUtils.RestrictedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.RestrictedPtr, ",")...) + } + + if len(*eUtils.ProtectedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.ProtectedPtr, ",")...) + } + + var listValues *api.Secret + if len(configCtx.ProjectSectionsSlice) > 0 { //If eid -> look inside Index and grab all environments + subSectionPath := configCtx.ProjectSectionsSlice[0] + "/" + listValues, err = testMod.ListEnv("super-secrets/"+testMod.Env+sectionKey+subSectionPath, driverConfig.CoreConfig.Log) + if err != nil { + if strings.Contains(err.Error(), "permission denied") { + eUtils.LogErrorMessage(&driverConfig.CoreConfig, "Attempt to access restricted section of the vault denied.", true) + } + } + + // Further path modifications needed. + if listValues == nil { + eUtils.LogAndSafeExit(&driverConfig.CoreConfig, "No available indexes found for "+subSectionPath, 1) + } + for k, valuesPath := range listValues.Data { + for _, indexNameInterface := range valuesPath.([]interface{}) { + if indexNameInterface != (subSectionName + "/") { + continue + } + indexList, err := testMod.ListEnv("super-secrets/"+testMod.Env+sectionKey+subSectionPath+"/"+indexNameInterface.(string), driverConfig.CoreConfig.Log) + if err != nil { + logger.Printf(err.Error()) + } + + for _, indexPath := range indexList.Data { + for _, indexInterface := range indexPath.([]interface{}) { + if len(*eUtils.IndexValueFilterPtr) > 0 { + if indexInterface != (*eUtils.IndexValueFilterPtr + "/") { + continue + } + } + newSectionSlice = append(newSectionSlice, strings.ReplaceAll(indexInterface.(string), "/", "")) + } + } + } + delete(listValues.Data, k) //delete it so it doesn't repeat below + } + } else { + listValues, err = testMod.ListEnv("values/", driverConfig.CoreConfig.Log) + } + if err != nil { + logger.Printf(err.Error()) + } + if len(newSectionSlice) > 0 { + sectionSlice = newSectionSlice + } + if testMod != nil { + testMod.Release() + } + } else { //novault takes this path + if len(*eUtils.IndexedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) + } + + if len(*eUtils.RestrictedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.RestrictedPtr, ",")...) + } + + if len(*eUtils.ProtectedPtr) > 0 { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.ProtectedPtr, ",")...) + } + } + } + } + + var filteredSectionSlice []string + + if len(*eUtils.IndexValueFilterPtr) > 0 { + filterSlice := strings.Split(*eUtils.IndexValueFilterPtr, ",") + for _, filter := range filterSlice { + for _, section := range sectionSlice { + if filter == section { + filteredSectionSlice = append(filteredSectionSlice, section) + } + } + } + sectionSlice = filteredSectionSlice + } + if len(*eUtils.ServiceFilterPtr) > 0 { + if len(sectionSlice) == 0 { + eUtils.LogAndSafeExit(&driverConfig.CoreConfig, "No available indexes found for "+*eUtils.IndexValueFilterPtr, 1) + } + serviceFilterSlice = strings.Split(*eUtils.ServiceFilterPtr, ",") + if len(*eUtils.ServiceNameFilterPtr) > 0 { + *eUtils.ServiceNameFilterPtr = "/" + *eUtils.ServiceNameFilterPtr //added "/" - used path later + } + } + } + + go receiver(configCtx) //Channel receiver + if len(*dynamicPathPtr) == 0 { + for _, env := range configCtx.EnvSlice { + envVersion := eUtils.SplitEnv(env) + *envPtr = envVersion[0] + if secretIDPtr != nil && *secretIDPtr != "" && appRoleIDPtr != nil && *appRoleIDPtr != "" { + *tokenPtr = "" + } + for _, section := range sectionSlice { + var servicesWanted []string + if !*noVaultPtr && *tokenPtr == "" { + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr}, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + // Retry once. + authErr := eUtils.AutoAuth(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + Log: logger, + }, + Insecure: *insecurePtr, + }, secretIDPtr, appRoleIDPtr, tokenPtr, tokenNamePtr, envPtr, addrPtr, envCtxPtr, "", *pingPtr) + if authErr != nil { + eUtils.LogAndSafeExit(&driverConfig.CoreConfig, fmt.Sprintf("Unexpected auth error %v ", authErr), 1) + } + } + } else if *tokenPtr == "" { + *tokenPtr = "novault" + } + if len(envVersion) >= 2 { //Put back env+version together + *envPtr = envVersion[0] + "_" + envVersion[1] + } else { + *envPtr = envVersion[0] + "_0" + } + + var trcxeList []string + if trcxe { + configCtx.ProjectSectionsSlice = append(configCtx.ProjectSectionsSlice, strings.Split(*eUtils.IndexedPtr, ",")...) + + trcxeList = append(trcxeList, *fieldsPtr) + trcxeList = append(trcxeList, *encryptedPtr) + if *noVaultPtr { + trcxeList = append(trcxeList, "new") + } + } + driverConfig := &eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + WantCerts: *wantCertsPtr, + DynamicPathFilter: *dynamicPathPtr, + ExitOnFailure: true, + Log: logger, + }, + Context: ctx, + Insecure: *insecurePtr, + Token: *tokenPtr, + VaultAddress: *addrPtr, + EnvBasis: envBasis, + Env: *envPtr, + SectionKey: sectionKey, + SectionName: subSectionName, + SubSectionValue: section, + SubSectionName: *eUtils.ServiceNameFilterPtr, + Regions: regions, + SecretMode: *secretMode, + ServicesWanted: servicesWanted, + StartDir: append([]string{}, *startDirPtr), + EndDir: *endDirPtr, + GenAuth: *genAuth, + Clean: *cleanPtr, + Diff: *diffPtr, + Update: messenger, + VersionInfo: eUtils.VersionHelper, + FileFilter: fileFilter, + SubPathFilter: strings.Split(*eUtils.SubPathFilter, ","), + ProjectSections: configCtx.ProjectSectionsSlice, + ServiceFilter: serviceFilterSlice, + Trcxe: trcxeList, + Trcxr: *readOnlyPtr, + } + waitg.Add(1) + go func(dc *eUtils.DriverConfig) { + defer waitg.Done() + eUtils.ConfigControl(ctx, configCtx, dc, configDriver) + }(driverConfig) + } + } + } + + waitg.Wait() + close(configCtx.ResultChannel) + if *diffPtr { //Diff if needed + waitg.Add(1) + go func(cctx *eUtils.ConfigContext) { + defer waitg.Done() + retry := 0 + for { + cctx.Mutex.Lock() + if len(cctx.ResultMap) == len(cctx.EnvSlice)*len(sectionSlice) || retry == 3 { + cctx.Mutex.Unlock() + break + } + cctx.Mutex.Unlock() + time.Sleep(time.Duration(time.Second)) + retry++ + } + configCtx.FileSysIndex = -1 + cctx.SetDiffFileCount(len(configCtx.ResultMap) / configCtx.EnvLength) + eUtils.DiffHelper(cctx, false) + }(configCtx) + } + waitg.Wait() //Wait for diff + + logger.SetPrefix("[" + coreopts.BuildOptions.GetFolderPrefix(nil) + "x]") + logger.Println("=============== Terminating Seed Generator ===============") + logger.SetPrefix("[END]") + logger.Println() +} diff --git a/pkg/trcinit/initlib/vault-seed.go b/pkg/trcinit/initlib/vault-seed.go index 3cf0cadd0..16d1a9c1b 100644 --- a/pkg/trcinit/initlib/vault-seed.go +++ b/pkg/trcinit/initlib/vault-seed.go @@ -678,8 +678,8 @@ func SeedVaultFromData(driverConfig *eUtils.DriverConfig, filepath string, fData return eUtils.LogAndSafeExit(&driverConfig.CoreConfig, "Invalid yaml file. Refusing to continue.", 1) } - mapStack := []seedCollection{seedCollection{"", seed}} // Begin with root of yaml file - writeStack := make([]writeCollection, 0) // List of all values to write to the vault with p + mapStack := []seedCollection{{"", seed}} // Begin with root of yaml file + writeStack := make([]writeCollection, 0) // List of all values to write to the vault with p // While the stack is not empty for len(mapStack) > 0 { diff --git a/pkg/trcx/xencrypt/xencrypt.go b/pkg/trcx/xencrypt/xencrypt.go index 5b25f44f5..81cdf8380 100644 --- a/pkg/trcx/xencrypt/xencrypt.go +++ b/pkg/trcx/xencrypt/xencrypt.go @@ -18,7 +18,7 @@ func FieldValidator(fields string, secSection map[string]map[string]map[string]s for _, valueField := range valueFields { valFieldMap[valueField] = false } - for valueField, _ := range valFieldMap { + for valueField := range valFieldMap { for secretSectionMap := range secSection["super-secrets"] { if _, ok := secSection["super-secrets"][secretSectionMap][valueField]; ok { valFieldMap[valueField] = true @@ -108,7 +108,7 @@ func CreateEncryptedReadMap(encryptedKeys string) map[string]interface{} { } func FieldReader(encryptedMap map[string]interface{}, secSection map[string]map[string]map[string]string, valSection map[string]map[string]map[string]string, decryption map[string]interface{}) error { - for field, _ := range encryptedMap { + for field := range encryptedMap { found := false for secretSectionMap := range secSection["super-secrets"] { if secretVal, ok := secSection["super-secrets"][secretSectionMap][field]; ok { diff --git a/pkg/utils/defaultTemplateParser.go b/pkg/utils/defaultTemplateParser.go index 0d7698ff7..c24a534aa 100644 --- a/pkg/utils/defaultTemplateParser.go +++ b/pkg/utils/defaultTemplateParser.go @@ -1,39 +1,39 @@ -package utils - -import ( - "os" - "regexp" - "strings" -) - -// {{or . ""}} -const pattern string = `{{or \.([^"]+) "([^"]+)"}}` - -// Parse Extracts default values as key-value pairs from template files. -// Before being uploaded, the service and filename will be appended so the uploaded value will be -// .. -// Underscores in key names will be replaced with periods before being uploaded -func Parse(filepath string, service string, filename string) (map[string]interface{}, error) { - workingSet := make(map[string]interface{}) - file, err := os.ReadFile(filepath) - if err != nil { - return nil, err - } - - regex, err := regexp.Compile(pattern) - - if err != nil { - return nil, err - } - - matched := regex.FindAllString(string(file), -1) - - for _, match := range matched { - kv := regex.FindStringSubmatch(match) - // Split and add to map - kv[1] = strings.Replace(kv[1], "_", ".", -1) - workingSet[kv[1]] = kv[2] - } - - return workingSet, nil -} +package utils + +import ( + "os" + "regexp" + "strings" +) + +// {{or . ""}} +const pattern string = `{{or \.([^"]+) "([^"]+)"}}` + +// Parse Extracts default values as key-value pairs from template files. +// Before being uploaded, the service and filename will be appended so the uploaded value will be +// .. +// Underscores in key names will be replaced with periods before being uploaded +func Parse(filepath string, service string, filename string) (map[string]interface{}, error) { + workingSet := make(map[string]interface{}) + file, err := os.ReadFile(filepath) + if err != nil { + return nil, err + } + + regex, err := regexp.Compile(pattern) + + if err != nil { + return nil, err + } + + matched := regex.FindAllString(string(file), -1) + + for _, match := range matched { + kv := regex.FindStringSubmatch(match) + // Split and add to map + kv[1] = strings.Replace(kv[1], "_", ".", -1) + workingSet[kv[1]] = kv[2] + } + + return workingSet, nil +} diff --git a/pkg/utils/diffUtil.go b/pkg/utils/diffUtil.go index f34b98d06..7bc553272 100644 --- a/pkg/utils/diffUtil.go +++ b/pkg/utils/diffUtil.go @@ -1,590 +1,590 @@ -package utils - -import ( - "bytes" - "fmt" - "log" - "math" - "net/url" - "sort" - "strconv" - "strings" - "time" - - "github.com/sergi/go-diff/diffmatchpatch" -) - -func GetStringInBetween(str string, start string, end string) (result string) { - s := strings.Index(str, start) - if s == -1 { - return - } - s += len(start) - e := strings.Index(str[s:], end) - if e == -1 { - return - } - return str[s : s+e] -} - -func LineByLineDiff(stringA *string, stringB *string, patchData bool, colorSkip bool) string { - //Colors used for output - var Reset = "\033[0m" - var Red = "\033[31m" - var Green = "\033[32m" - var Cyan = "\033[36m" - var result string - - if IsWindows() { - Reset = "\x1b[0m" - Red = "\x1b[31m" - Green = "\x1b[32m" - Cyan = "\x1b[36m" - } else if colorSkip { - Reset = "" - Red = "" - Green = "" - Cyan = "" - } - - dmp := diffmatchpatch.New() - var patchOutput string - if patchData { - var patchText string - //Patch Calculation - Catches patch slice out of bounds - func() { - defer func() { - if r := recover(); r != nil { - patchText = "" - } - }() - patches := dmp.PatchMake(*stringA, *stringB) //This throws out of index slice error rarely - patchText = dmp.PatchToText(patches) - }() - - if patchText != "" { - //Converts escaped chars in patches - unescapedPatchText, err2 := url.PathUnescape(patchText) - if err2 != nil { - log.Fatalf("Unable to decode percent-encoding: %v", err2) - } - - parsedPatchText := strings.Split(unescapedPatchText, "\n") - - //Fixes char offset due to common preString - for i, string := range parsedPatchText { - if strings.Contains(string, "@@") { - charOffset := string[strings.Index(parsedPatchText[i], "-")+1 : strings.Index(parsedPatchText[i], ",")] - charOffsetInt, _ := strconv.Atoi(charOffset) - charOffsetInt = charOffsetInt - 2 + len(parsedPatchText[i+1]) - parsedPatchText[i] = strings.Replace(string, charOffset, strconv.Itoa(charOffsetInt), 2) - } - } - - //Grabs only patch data from PatchMake - onlyPatchedText := []string{} - for _, stringLine := range parsedPatchText { - if strings.Contains(stringLine, "@@") { - onlyPatchedText = append(onlyPatchedText, stringLine) - } - } - - //Patch Data Output - patchOutput = Cyan + strings.Join(onlyPatchedText, " ") + Reset + "\n" - } else { - patchOutput = Cyan + "@@ Patch Data Unavailable @@" + Reset + "\n" - } - } - - //Diff Calculation - diffTimeout := false - timeOut := time.Now().Add(time.Minute * 1) - if stringA == nil || stringB == nil { - fmt.Println("A null string was found while diffing") - return "" - } - diffs := dmp.DiffBisect(*stringA, *stringB, timeOut) - diffs = dmp.DiffCleanupSemantic(diffs) - - if time.Now().After(timeOut) { - diffTimeout = true - diffs = diffs[:0] - } - - //Separates diff into red and green lines - var redBuffer bytes.Buffer - var greenBuffer bytes.Buffer - for _, diff := range diffs { - text := diff.Text - switch diff.Type { - case diffmatchpatch.DiffDelete: - _, _ = greenBuffer.WriteString(Green) - _, _ = greenBuffer.WriteString(text) - _, _ = greenBuffer.WriteString(Reset) - case diffmatchpatch.DiffInsert: - _, _ = redBuffer.WriteString(Red) - _, _ = redBuffer.WriteString(text) - _, _ = redBuffer.WriteString(Reset) - case diffmatchpatch.DiffEqual: - _, _ = redBuffer.WriteString(text) - _, _ = greenBuffer.WriteString(text) - } - } - - greenLineSplit := strings.Split(greenBuffer.String(), "\n") - redLineSplit := strings.Split(redBuffer.String(), "\n") - - //Adds + for each green line - for greenIndex, greenLine := range greenLineSplit { - if strings.Contains(greenLine, Green) { - greenLineSplit[greenIndex] = "+" + greenLine - } - } - - //Adds - for each red line - for redIndex, redLine := range redLineSplit { - if strings.Contains(redLine, Red) { - redLineSplit[redIndex] = "-" + redLine - } - } - - //Red vs Green length - lengthDiff := 0 - sameLength := 0 - var redSwitch bool - if len(redLineSplit) > len(greenLineSplit) { - redSwitch = true - lengthDiff = len(redLineSplit) - len(greenLineSplit) - sameLength = len(greenLineSplit) - } else { //Green > Red - redSwitch = false - lengthDiff = len(greenLineSplit) - len(redLineSplit) - sameLength = len(redLineSplit) - } - - //Prints line-by-line until shorter length - currentIndex := 0 - for currentIndex != sameLength { - redLine := redLineSplit[currentIndex] - greenLine := greenLineSplit[currentIndex] - if len(redLine) > 0 && redLine[0] == '-' { - result += redLine + "\n" - } - if len(greenLine) > 0 && greenLine[0] == '+' { - result += greenLine + "\n" - } - currentIndex++ - } - - //Prints rest of longer length - for currentIndex != lengthDiff+sameLength { - if redSwitch { - redLine := redLineSplit[currentIndex] - if len(redLine) > 0 && redLine[0] == '-' { - result += redLine + "\n" - } - } else { - greenLine := greenLineSplit[currentIndex] - if len(greenLine) > 0 && greenLine[0] == '+' { - result += greenLine + "\n" - } - } - currentIndex++ - } - - //Colors first line "+" & "-" - if len(result) > 0 && string(result[0]) == "+" { - result = strings.Replace(result, "+", Green+"+"+Reset, 1) - } else if len(result) > 0 && string(result[0]) == "-" { - result = strings.Replace(result, "-", Red+"-"+Reset, 1) - } - - //Colors all "+" & "-" using previous newline - result = strings.ReplaceAll(result, "\n", Reset+"\n") - result = strings.ReplaceAll(result, "\n+", "\n"+Green+"+"+Reset) - result = strings.ReplaceAll(result, "\n-", "\n"+Red+"-"+Reset) - - //Diff vs no Diff output - if len(strings.TrimSpace(result)) == 0 && patchData { - if diffTimeout { - if IsWindows() { - return "@@ Diff Timed Out @@" - } - return Cyan + "@@ Diff Timed Out @@" + Reset - } - - if IsWindows() { - return "@@ No Differences @@" - } - return Cyan + "@@ No Differences @@" + Reset - } else { - if patchOutput != "" { - result = patchOutput + result - } - result = strings.TrimSuffix(result, "\n") - } - - if IsWindows() { - result = strings.ReplaceAll(result, Reset, "") - result = strings.ReplaceAll(result, Green, "") - result = strings.ReplaceAll(result, Cyan, "") - result = strings.ReplaceAll(result, Red, "") - } - - return result -} - -func VersionHelper(versionData map[string]interface{}, templateOrValues bool, valuePath string, first bool) { - Reset := "\033[0m" - Cyan := "\033[36m" - Red := "\033[31m" - if IsWindows() { - Reset = "" - Cyan = "" - Red = "" - } - - if versionData == nil { - fmt.Println("No version data found for this environment") - return - } - - //template == true - if templateOrValues { - for _, versionMap := range versionData { - for _, versionMetadata := range versionMap.(map[string]interface{}) { - for field, data := range versionMetadata.(map[string]interface{}) { - if field == "destroyed" && !data.(bool) { - goto printOutput1 - } - } - } - } - return - - printOutput1: - for filename, versionMap := range versionData { - fmt.Println(Cyan + "======================================================================================") - fmt.Println(filename) - fmt.Println("======================================================================================" + Reset) - keys := make([]int, 0, len(versionMap.(map[string]interface{}))) - for versionNumber := range versionMap.(map[string]interface{}) { - versionNo, err := strconv.Atoi(versionNumber) - if err != nil { - fmt.Println() - } - keys = append(keys, versionNo) - } - sort.Ints(keys) - for i, key := range keys { - versionNumber := fmt.Sprint(key) - versionMetadata := versionMap.(map[string]interface{})[fmt.Sprint(key)] - fmt.Println("Version " + string(versionNumber) + " Metadata:") - - fields := make([]string, 0, len(versionMetadata.(map[string]interface{}))) - for field := range versionMetadata.(map[string]interface{}) { - fields = append(fields, field) - } - sort.Strings(fields) - for _, field := range fields { - fmt.Printf(field + ": ") - fmt.Println(versionMetadata.(map[string]interface{})[field]) - } - if i != len(keys)-1 { - fmt.Println(Red + "-------------------------------------------------------------------------------" + Reset) - } - } - } - fmt.Println(Cyan + "======================================================================================" + Reset) - } else { - for _, versionMetadata := range versionData { - for field, data := range versionMetadata.(map[string]interface{}) { - if field == "destroyed" && !data.(bool) { - goto printOutput - } - } - } - return - - printOutput: - if len(valuePath) > 0 { - if first { - fmt.Println(Cyan + "======================================================================================" + Reset) - } - fmt.Println(valuePath) - } - - fmt.Println(Cyan + "======================================================================================" + Reset) - - keys := make([]int, 0, len(versionData)) - for versionNumber := range versionData { - versionNo, err := strconv.ParseInt(versionNumber, 10, 64) - if err == nil && versionNo <= math.MaxInt { - keys = append(keys, int(versionNo)) - } else { - fmt.Printf("Version limit exceeded: %s\n", versionNumber) - return - } - } - sort.Ints(keys) - for _, key := range keys { - versionNumber := key - versionMetadata := versionData[fmt.Sprint(key)] - fields := make([]string, 0) - fieldData := make(map[string]interface{}, 0) - for field, data := range versionMetadata.(map[string]interface{}) { - fields = append(fields, field) - fieldData[field] = data - } - sort.Strings(fields) - fmt.Println("Version " + fmt.Sprint(versionNumber) + " Metadata:") - for _, field := range fields { - fmt.Printf(field + ": ") - fmt.Println(fieldData[field]) - } - if keys[len(keys)-1] != versionNumber { - fmt.Println(Red + "-------------------------------------------------------------------------------" + Reset) - } - } - fmt.Println(Cyan + "======================================================================================" + Reset) - } -} - -func RemoveDuplicateValues(intSlice []string) []string { - keys := make(map[string]bool) - list := []string{} - - for _, entry := range intSlice { - if _, value := keys[entry]; !value { - keys[entry] = true - list = append(list, entry) - } - } - return list -} - -func DiffHelper(configCtx *ConfigContext, config bool) { - fileIndex := 0 - keys := []string{} - configCtx.Mutex.Lock() - if len(configCtx.ResultMap) == 0 { - fmt.Println("Couldn't find any data to diff") - return - } - - var baseEnv []string - diffEnvFound := false - if len(configCtx.EnvSlice) > 0 { - baseEnv = SplitEnv(configCtx.EnvSlice[0]) - } - //Sort Diff Slice if env are the same - for i, env := range configCtx.EnvSlice { //Arranges keys for ordered output - var base []string = SplitEnv(env) - - if base[1] == "0" { //Special case for latest, so sort adds latest to the back of ordered slice - base[1] = "_999999" - configCtx.EnvSlice[i] = base[0] + base[1] - } - - if len(base) > 0 && len(baseEnv) > 0 && baseEnv[0] != base[0] { - diffEnvFound = true - } - } - - if !diffEnvFound { - sort.Strings(configCtx.EnvSlice) - } - - for i, env := range configCtx.EnvSlice { //Changes latest back - special case - var base []string = SplitEnv(env) - if base[1] == "999999" { - base[1] = "_0" - configCtx.EnvSlice[i] = base[0] + base[1] - } - } - - fileList := make([]string, configCtx.DiffFileCount) - configCtx.Mutex.Unlock() - - sleepCount := 0 - if len(configCtx.ResultMap) != int(configCtx.DiffFileCount) { - for { - time.Sleep(time.Second) - sleepCount++ - if sleepCount >= 5 { - fmt.Println("Timeout: Attempted to wait for remaining configs to come in. Attempting incomplete diff.") - break - } else if len(configCtx.ResultMap) == int(configCtx.DiffFileCount)*configCtx.EnvLength { - break - } - } - } - - if config { - //Make fileList - for key, _ := range configCtx.ResultMap { - found := false - keySplit := strings.Split(key, "||") - - for _, fileName := range fileList { - if fileName == keySplit[1] { - found = true - } - } - - if !found && len(fileList) > 0 && fileIndex < len(fileList) { - fileList[fileIndex] = keySplit[1] - fileIndex++ - } - } - } else { - for _, env := range configCtx.EnvSlice { //Arranges keys for ordered output - keys = append(keys, env+"||"+env+"_seed.yml") - } - if len(fileList) > 0 { - fileList[0] = "placeHolder" - } else { - fileList = append(fileList, "placeHolder") - } - } - - //Diff resultMap using fileList - for _, fileName := range fileList { - if config { - //Arranges keys for ordered output - for _, env := range configCtx.EnvSlice { - keys = append(keys, env+"||"+fileName) - } - if configCtx.FileSysIndex == len(configCtx.EnvSlice) { - keys = append(keys, "filesys||"+fileName) - } - } - - Reset := "\033[0m" - Red := "\033[31m" - Green := "\033[32m" - Yellow := "\033[0;33m" - - if IsWindows() { - Reset = "" - Red = "" - Green = "" - Yellow = "" - } - - keyA := keys[0] - keyB := keys[1] - keySplitA := strings.Split(keyA, "||") - keySplitB := strings.Split(keyB, "||") - configCtx.Mutex.Lock() - - sortedKeyA := keyA - sortedKeyB := keyB - if _, ok := configCtx.ResultMap[sortedKeyA]; !ok { - sortedKeyA = "||" + keySplitA[1] - } - if _, ok := configCtx.ResultMap[sortedKeyB]; !ok { - sortedKeyB = "||" + keySplitB[1] - } - - envFileKeyA := configCtx.ResultMap[sortedKeyA] - envFileKeyB := configCtx.ResultMap[sortedKeyB] - configCtx.Mutex.Unlock() - - latestVersionACheck := strings.Split(keySplitA[0], "_") - if len(latestVersionACheck) > 1 && latestVersionACheck[1] == "0" { - keySplitA[0] = strings.ReplaceAll(keySplitA[0], "0", "latest") - } - latestVersionBCheck := strings.Split(keySplitB[0], "_") - if len(latestVersionBCheck) > 1 && latestVersionBCheck[1] == "0" { - keySplitB[0] = strings.ReplaceAll(keySplitB[0], "0", "latest") - } - - if strings.Count(keySplitA[1], "_") == 2 { - fileSplit := strings.Split(keySplitA[1], "_") - keySplitA[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] - } - - if strings.Count(keySplitB[1], "_") == 2 { - fileSplit := strings.Split(keySplitB[1], "_") - keySplitB[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] - } - switch configCtx.EnvLength { - case 4: - keyC := keys[2] - keyD := keys[3] - keySplitC := strings.Split(keyC, "||") - keySplitD := strings.Split(keyD, "||") - configCtx.Mutex.Lock() - envFileKeyC := configCtx.ResultMap[keyC] - envFileKeyD := configCtx.ResultMap[keyD] - configCtx.Mutex.Unlock() - - latestVersionCCheck := strings.Split(keySplitC[0], "_") - if len(latestVersionCCheck) > 1 && latestVersionCCheck[1] == "0" { - keySplitC[0] = strings.ReplaceAll(keySplitC[0], "0", "latest") - } - latestVersionDCheck := strings.Split(keySplitD[0], "_") - if len(latestVersionDCheck) > 1 && latestVersionDCheck[1] == "0" { - keySplitD[0] = strings.ReplaceAll(keySplitD[0], "0", "latest") - } - - if strings.Count(keySplitC[1], "_") == 2 { - fileSplit := strings.Split(keySplitC[1], "_") - keySplitC[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] - } - - if strings.Count(keySplitD[1], "_") == 2 { - fileSplit := strings.Split(keySplitD[1], "_") - keySplitD[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] - } - - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyA, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyA, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyB, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyB, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitC[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyC, true, false)) - case 3: - keyC := keys[2] - keySplitC := strings.Split(keyC, "||") - configCtx.Mutex.Lock() - envFileKeyC := configCtx.ResultMap[keyC] - configCtx.Mutex.Unlock() - - latestVersionCCheck := strings.Split(keySplitC[0], "_") - if len(latestVersionCCheck) > 1 && latestVersionCCheck[1] == "0" { - keySplitC[0] = strings.ReplaceAll(keySplitC[0], "0", "latest") - } - - if strings.Count(keySplitC[1], "_") == 2 { - fileSplit := strings.Split(keySplitC[1], "_") - keySplitC[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] - } - - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyA, true, false)) - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyB, true, false)) - default: - fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") - fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) - } - - //Seperator - if IsWindows() { - fmt.Printf("======================================================================================\n") - } else { - fmt.Printf("\033[1;35m======================================================================================\033[0m\n") - } - keys = keys[:0] //Cleans keys for next file - } -} +package utils + +import ( + "bytes" + "fmt" + "log" + "math" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/sergi/go-diff/diffmatchpatch" +) + +func GetStringInBetween(str string, start string, end string) (result string) { + s := strings.Index(str, start) + if s == -1 { + return + } + s += len(start) + e := strings.Index(str[s:], end) + if e == -1 { + return + } + return str[s : s+e] +} + +func LineByLineDiff(stringA *string, stringB *string, patchData bool, colorSkip bool) string { + //Colors used for output + var Reset = "\033[0m" + var Red = "\033[31m" + var Green = "\033[32m" + var Cyan = "\033[36m" + var result string + + if IsWindows() { + Reset = "\x1b[0m" + Red = "\x1b[31m" + Green = "\x1b[32m" + Cyan = "\x1b[36m" + } else if colorSkip { + Reset = "" + Red = "" + Green = "" + Cyan = "" + } + + dmp := diffmatchpatch.New() + var patchOutput string + if patchData { + var patchText string + //Patch Calculation - Catches patch slice out of bounds + func() { + defer func() { + if r := recover(); r != nil { + patchText = "" + } + }() + patches := dmp.PatchMake(*stringA, *stringB) //This throws out of index slice error rarely + patchText = dmp.PatchToText(patches) + }() + + if patchText != "" { + //Converts escaped chars in patches + unescapedPatchText, err2 := url.PathUnescape(patchText) + if err2 != nil { + log.Fatalf("Unable to decode percent-encoding: %v", err2) + } + + parsedPatchText := strings.Split(unescapedPatchText, "\n") + + //Fixes char offset due to common preString + for i, string := range parsedPatchText { + if strings.Contains(string, "@@") { + charOffset := string[strings.Index(parsedPatchText[i], "-")+1 : strings.Index(parsedPatchText[i], ",")] + charOffsetInt, _ := strconv.Atoi(charOffset) + charOffsetInt = charOffsetInt - 2 + len(parsedPatchText[i+1]) + parsedPatchText[i] = strings.Replace(string, charOffset, strconv.Itoa(charOffsetInt), 2) + } + } + + //Grabs only patch data from PatchMake + onlyPatchedText := []string{} + for _, stringLine := range parsedPatchText { + if strings.Contains(stringLine, "@@") { + onlyPatchedText = append(onlyPatchedText, stringLine) + } + } + + //Patch Data Output + patchOutput = Cyan + strings.Join(onlyPatchedText, " ") + Reset + "\n" + } else { + patchOutput = Cyan + "@@ Patch Data Unavailable @@" + Reset + "\n" + } + } + + //Diff Calculation + diffTimeout := false + timeOut := time.Now().Add(time.Minute * 1) + if stringA == nil || stringB == nil { + fmt.Println("A null string was found while diffing") + return "" + } + diffs := dmp.DiffBisect(*stringA, *stringB, timeOut) + diffs = dmp.DiffCleanupSemantic(diffs) + + if time.Now().After(timeOut) { + diffTimeout = true + diffs = diffs[:0] + } + + //Separates diff into red and green lines + var redBuffer bytes.Buffer + var greenBuffer bytes.Buffer + for _, diff := range diffs { + text := diff.Text + switch diff.Type { + case diffmatchpatch.DiffDelete: + _, _ = greenBuffer.WriteString(Green) + _, _ = greenBuffer.WriteString(text) + _, _ = greenBuffer.WriteString(Reset) + case diffmatchpatch.DiffInsert: + _, _ = redBuffer.WriteString(Red) + _, _ = redBuffer.WriteString(text) + _, _ = redBuffer.WriteString(Reset) + case diffmatchpatch.DiffEqual: + _, _ = redBuffer.WriteString(text) + _, _ = greenBuffer.WriteString(text) + } + } + + greenLineSplit := strings.Split(greenBuffer.String(), "\n") + redLineSplit := strings.Split(redBuffer.String(), "\n") + + //Adds + for each green line + for greenIndex, greenLine := range greenLineSplit { + if strings.Contains(greenLine, Green) { + greenLineSplit[greenIndex] = "+" + greenLine + } + } + + //Adds - for each red line + for redIndex, redLine := range redLineSplit { + if strings.Contains(redLine, Red) { + redLineSplit[redIndex] = "-" + redLine + } + } + + //Red vs Green length + lengthDiff := 0 + sameLength := 0 + var redSwitch bool + if len(redLineSplit) > len(greenLineSplit) { + redSwitch = true + lengthDiff = len(redLineSplit) - len(greenLineSplit) + sameLength = len(greenLineSplit) + } else { //Green > Red + redSwitch = false + lengthDiff = len(greenLineSplit) - len(redLineSplit) + sameLength = len(redLineSplit) + } + + //Prints line-by-line until shorter length + currentIndex := 0 + for currentIndex != sameLength { + redLine := redLineSplit[currentIndex] + greenLine := greenLineSplit[currentIndex] + if len(redLine) > 0 && redLine[0] == '-' { + result += redLine + "\n" + } + if len(greenLine) > 0 && greenLine[0] == '+' { + result += greenLine + "\n" + } + currentIndex++ + } + + //Prints rest of longer length + for currentIndex != lengthDiff+sameLength { + if redSwitch { + redLine := redLineSplit[currentIndex] + if len(redLine) > 0 && redLine[0] == '-' { + result += redLine + "\n" + } + } else { + greenLine := greenLineSplit[currentIndex] + if len(greenLine) > 0 && greenLine[0] == '+' { + result += greenLine + "\n" + } + } + currentIndex++ + } + + //Colors first line "+" & "-" + if len(result) > 0 && string(result[0]) == "+" { + result = strings.Replace(result, "+", Green+"+"+Reset, 1) + } else if len(result) > 0 && string(result[0]) == "-" { + result = strings.Replace(result, "-", Red+"-"+Reset, 1) + } + + //Colors all "+" & "-" using previous newline + result = strings.ReplaceAll(result, "\n", Reset+"\n") + result = strings.ReplaceAll(result, "\n+", "\n"+Green+"+"+Reset) + result = strings.ReplaceAll(result, "\n-", "\n"+Red+"-"+Reset) + + //Diff vs no Diff output + if len(strings.TrimSpace(result)) == 0 && patchData { + if diffTimeout { + if IsWindows() { + return "@@ Diff Timed Out @@" + } + return Cyan + "@@ Diff Timed Out @@" + Reset + } + + if IsWindows() { + return "@@ No Differences @@" + } + return Cyan + "@@ No Differences @@" + Reset + } else { + if patchOutput != "" { + result = patchOutput + result + } + result = strings.TrimSuffix(result, "\n") + } + + if IsWindows() { + result = strings.ReplaceAll(result, Reset, "") + result = strings.ReplaceAll(result, Green, "") + result = strings.ReplaceAll(result, Cyan, "") + result = strings.ReplaceAll(result, Red, "") + } + + return result +} + +func VersionHelper(versionData map[string]interface{}, templateOrValues bool, valuePath string, first bool) { + Reset := "\033[0m" + Cyan := "\033[36m" + Red := "\033[31m" + if IsWindows() { + Reset = "" + Cyan = "" + Red = "" + } + + if versionData == nil { + fmt.Println("No version data found for this environment") + return + } + + //template == true + if templateOrValues { + for _, versionMap := range versionData { + for _, versionMetadata := range versionMap.(map[string]interface{}) { + for field, data := range versionMetadata.(map[string]interface{}) { + if field == "destroyed" && !data.(bool) { + goto printOutput1 + } + } + } + } + return + + printOutput1: + for filename, versionMap := range versionData { + fmt.Println(Cyan + "======================================================================================") + fmt.Println(filename) + fmt.Println("======================================================================================" + Reset) + keys := make([]int, 0, len(versionMap.(map[string]interface{}))) + for versionNumber := range versionMap.(map[string]interface{}) { + versionNo, err := strconv.Atoi(versionNumber) + if err != nil { + fmt.Println() + } + keys = append(keys, versionNo) + } + sort.Ints(keys) + for i, key := range keys { + versionNumber := fmt.Sprint(key) + versionMetadata := versionMap.(map[string]interface{})[fmt.Sprint(key)] + fmt.Println("Version " + string(versionNumber) + " Metadata:") + + fields := make([]string, 0, len(versionMetadata.(map[string]interface{}))) + for field := range versionMetadata.(map[string]interface{}) { + fields = append(fields, field) + } + sort.Strings(fields) + for _, field := range fields { + fmt.Printf(field + ": ") + fmt.Println(versionMetadata.(map[string]interface{})[field]) + } + if i != len(keys)-1 { + fmt.Println(Red + "-------------------------------------------------------------------------------" + Reset) + } + } + } + fmt.Println(Cyan + "======================================================================================" + Reset) + } else { + for _, versionMetadata := range versionData { + for field, data := range versionMetadata.(map[string]interface{}) { + if field == "destroyed" && !data.(bool) { + goto printOutput + } + } + } + return + + printOutput: + if len(valuePath) > 0 { + if first { + fmt.Println(Cyan + "======================================================================================" + Reset) + } + fmt.Println(valuePath) + } + + fmt.Println(Cyan + "======================================================================================" + Reset) + + keys := make([]int, 0, len(versionData)) + for versionNumber := range versionData { + versionNo, err := strconv.ParseInt(versionNumber, 10, 64) + if err == nil && versionNo <= math.MaxInt { + keys = append(keys, int(versionNo)) + } else { + fmt.Printf("Version limit exceeded: %s\n", versionNumber) + return + } + } + sort.Ints(keys) + for _, key := range keys { + versionNumber := key + versionMetadata := versionData[fmt.Sprint(key)] + fields := make([]string, 0) + fieldData := make(map[string]interface{}, 0) + for field, data := range versionMetadata.(map[string]interface{}) { + fields = append(fields, field) + fieldData[field] = data + } + sort.Strings(fields) + fmt.Println("Version " + fmt.Sprint(versionNumber) + " Metadata:") + for _, field := range fields { + fmt.Printf(field + ": ") + fmt.Println(fieldData[field]) + } + if keys[len(keys)-1] != versionNumber { + fmt.Println(Red + "-------------------------------------------------------------------------------" + Reset) + } + } + fmt.Println(Cyan + "======================================================================================" + Reset) + } +} + +func RemoveDuplicateValues(intSlice []string) []string { + keys := make(map[string]bool) + list := []string{} + + for _, entry := range intSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func DiffHelper(configCtx *ConfigContext, config bool) { + fileIndex := 0 + keys := []string{} + configCtx.Mutex.Lock() + if len(configCtx.ResultMap) == 0 { + fmt.Println("Couldn't find any data to diff") + return + } + + var baseEnv []string + diffEnvFound := false + if len(configCtx.EnvSlice) > 0 { + baseEnv = SplitEnv(configCtx.EnvSlice[0]) + } + //Sort Diff Slice if env are the same + for i, env := range configCtx.EnvSlice { //Arranges keys for ordered output + var base []string = SplitEnv(env) + + if base[1] == "0" { //Special case for latest, so sort adds latest to the back of ordered slice + base[1] = "_999999" + configCtx.EnvSlice[i] = base[0] + base[1] + } + + if len(base) > 0 && len(baseEnv) > 0 && baseEnv[0] != base[0] { + diffEnvFound = true + } + } + + if !diffEnvFound { + sort.Strings(configCtx.EnvSlice) + } + + for i, env := range configCtx.EnvSlice { //Changes latest back - special case + var base []string = SplitEnv(env) + if base[1] == "999999" { + base[1] = "_0" + configCtx.EnvSlice[i] = base[0] + base[1] + } + } + + fileList := make([]string, configCtx.DiffFileCount) + configCtx.Mutex.Unlock() + + sleepCount := 0 + if len(configCtx.ResultMap) != int(configCtx.DiffFileCount) { + for { + time.Sleep(time.Second) + sleepCount++ + if sleepCount >= 5 { + fmt.Println("Timeout: Attempted to wait for remaining configs to come in. Attempting incomplete diff.") + break + } else if len(configCtx.ResultMap) == int(configCtx.DiffFileCount)*configCtx.EnvLength { + break + } + } + } + + if config { + //Make fileList + for key := range configCtx.ResultMap { + found := false + keySplit := strings.Split(key, "||") + + for _, fileName := range fileList { + if fileName == keySplit[1] { + found = true + } + } + + if !found && len(fileList) > 0 && fileIndex < len(fileList) { + fileList[fileIndex] = keySplit[1] + fileIndex++ + } + } + } else { + for _, env := range configCtx.EnvSlice { //Arranges keys for ordered output + keys = append(keys, env+"||"+env+"_seed.yml") + } + if len(fileList) > 0 { + fileList[0] = "placeHolder" + } else { + fileList = append(fileList, "placeHolder") + } + } + + //Diff resultMap using fileList + for _, fileName := range fileList { + if config { + //Arranges keys for ordered output + for _, env := range configCtx.EnvSlice { + keys = append(keys, env+"||"+fileName) + } + if configCtx.FileSysIndex == len(configCtx.EnvSlice) { + keys = append(keys, "filesys||"+fileName) + } + } + + Reset := "\033[0m" + Red := "\033[31m" + Green := "\033[32m" + Yellow := "\033[0;33m" + + if IsWindows() { + Reset = "" + Red = "" + Green = "" + Yellow = "" + } + + keyA := keys[0] + keyB := keys[1] + keySplitA := strings.Split(keyA, "||") + keySplitB := strings.Split(keyB, "||") + configCtx.Mutex.Lock() + + sortedKeyA := keyA + sortedKeyB := keyB + if _, ok := configCtx.ResultMap[sortedKeyA]; !ok { + sortedKeyA = "||" + keySplitA[1] + } + if _, ok := configCtx.ResultMap[sortedKeyB]; !ok { + sortedKeyB = "||" + keySplitB[1] + } + + envFileKeyA := configCtx.ResultMap[sortedKeyA] + envFileKeyB := configCtx.ResultMap[sortedKeyB] + configCtx.Mutex.Unlock() + + latestVersionACheck := strings.Split(keySplitA[0], "_") + if len(latestVersionACheck) > 1 && latestVersionACheck[1] == "0" { + keySplitA[0] = strings.ReplaceAll(keySplitA[0], "0", "latest") + } + latestVersionBCheck := strings.Split(keySplitB[0], "_") + if len(latestVersionBCheck) > 1 && latestVersionBCheck[1] == "0" { + keySplitB[0] = strings.ReplaceAll(keySplitB[0], "0", "latest") + } + + if strings.Count(keySplitA[1], "_") == 2 { + fileSplit := strings.Split(keySplitA[1], "_") + keySplitA[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] + } + + if strings.Count(keySplitB[1], "_") == 2 { + fileSplit := strings.Split(keySplitB[1], "_") + keySplitB[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] + } + switch configCtx.EnvLength { + case 4: + keyC := keys[2] + keyD := keys[3] + keySplitC := strings.Split(keyC, "||") + keySplitD := strings.Split(keyD, "||") + configCtx.Mutex.Lock() + envFileKeyC := configCtx.ResultMap[keyC] + envFileKeyD := configCtx.ResultMap[keyD] + configCtx.Mutex.Unlock() + + latestVersionCCheck := strings.Split(keySplitC[0], "_") + if len(latestVersionCCheck) > 1 && latestVersionCCheck[1] == "0" { + keySplitC[0] = strings.ReplaceAll(keySplitC[0], "0", "latest") + } + latestVersionDCheck := strings.Split(keySplitD[0], "_") + if len(latestVersionDCheck) > 1 && latestVersionDCheck[1] == "0" { + keySplitD[0] = strings.ReplaceAll(keySplitD[0], "0", "latest") + } + + if strings.Count(keySplitC[1], "_") == 2 { + fileSplit := strings.Split(keySplitC[1], "_") + keySplitC[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] + } + + if strings.Count(keySplitD[1], "_") == 2 { + fileSplit := strings.Split(keySplitD[1], "_") + keySplitD[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] + } + + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyA, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyA, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyB, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyB, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitC[0] + Reset + Green + " +Env-" + keySplitD[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyD, envFileKeyC, true, false)) + case 3: + keyC := keys[2] + keySplitC := strings.Split(keyC, "||") + configCtx.Mutex.Lock() + envFileKeyC := configCtx.ResultMap[keyC] + configCtx.Mutex.Unlock() + + latestVersionCCheck := strings.Split(keySplitC[0], "_") + if len(latestVersionCCheck) > 1 && latestVersionCCheck[1] == "0" { + keySplitC[0] = strings.ReplaceAll(keySplitC[0], "0", "latest") + } + + if strings.Count(keySplitC[1], "_") == 2 { + fileSplit := strings.Split(keySplitC[1], "_") + keySplitC[1] = fileSplit[0] + "_" + fileSplit[len(fileSplit)-1] + } + + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyA, true, false)) + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitB[0] + Reset + Green + " +Env-" + keySplitC[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyC, envFileKeyB, true, false)) + default: + fmt.Print("\n" + Yellow + keySplitA[1] + " (" + Reset + Red + "-Env-" + keySplitA[0] + Reset + Green + " +Env-" + keySplitB[0] + Reset + Yellow + ")" + Reset + "\n") + fmt.Println(LineByLineDiff(envFileKeyB, envFileKeyA, true, false)) + } + + //Seperator + if IsWindows() { + fmt.Printf("======================================================================================\n") + } else { + fmt.Printf("\033[1;35m======================================================================================\033[0m\n") + } + keys = keys[:0] //Cleans keys for next file + } +} diff --git a/pkg/utils/mlock/mlock_none.go b/pkg/utils/mlock/mlock_none.go index 47069696c..9a4f02ea9 100644 --- a/pkg/utils/mlock/mlock_none.go +++ b/pkg/utils/mlock/mlock_none.go @@ -1,41 +1,41 @@ -//go:build android || darwin || nacl || netbsd || plan9 || windows -// +build android darwin nacl netbsd plan9 windows - -package mlock - -import ( - "fmt" - "log" - "os" -) - -// Mlock - provides locking hook for OS's that don't support mlock -func Mlock(logger *log.Logger) error { - if logger != nil { - logger.Println("Mlock not supported.") - } else { - fmt.Println("Mlock not supported.") - } - os.Exit(1) - return nil -} - -func Mlock2(logger *log.Logger, sensitive *string) error { - if logger != nil { - logger.Println("Mlock2 not supported.") - } else { - fmt.Println("Mlock2 not supported.") - } - os.Exit(1) - return nil -} - -func MunlockAll(logger *log.Logger) error { - if logger != nil { - logger.Println("MunlockAll not supported.") - } else { - fmt.Println("MunlockAll not supported.") - } - os.Exit(1) - return nil -} +//go:build android || darwin || nacl || netbsd || plan9 || windows +// +build android darwin nacl netbsd plan9 windows + +package mlock + +import ( + "fmt" + "log" + "os" +) + +// Mlock - provides locking hook for OS's that don't support mlock +func Mlock(logger *log.Logger) error { + if logger != nil { + logger.Println("Mlock not supported.") + } else { + fmt.Println("Mlock not supported.") + } + os.Exit(1) + return nil +} + +func Mlock2(logger *log.Logger, sensitive *string) error { + if logger != nil { + logger.Println("Mlock2 not supported.") + } else { + fmt.Println("Mlock2 not supported.") + } + os.Exit(1) + return nil +} + +func MunlockAll(logger *log.Logger) error { + if logger != nil { + logger.Println("MunlockAll not supported.") + } else { + fmt.Println("MunlockAll not supported.") + } + os.Exit(1) + return nil +} diff --git a/pkg/utils/mlock/mlock_unix.go b/pkg/utils/mlock/mlock_unix.go index c7a8958bd..79c8e99b7 100644 --- a/pkg/utils/mlock/mlock_unix.go +++ b/pkg/utils/mlock/mlock_unix.go @@ -1,50 +1,50 @@ -//go:build dragonfly || freebsd || linux || openbsd || solaris -// +build dragonfly freebsd linux openbsd solaris - -package mlock - -import ( - "log" - "reflect" - "syscall" - "unsafe" - - "golang.org/x/sys/unix" -) - -// Mlock - provides locking hook for OS's that support mlock -func Mlock(logger *log.Logger) error { - return unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) -} - -var _zero uintptr - -func Mlock2(logger *log.Logger, sensitive *string) error { - var _p0 unsafe.Pointer - var err error = nil - sensitiveHeader := (*reflect.StringHeader)(unsafe.Pointer(sensitive)) - sensitiveLen := sensitiveHeader.Len - if sensitiveLen > 0 { - _p0 = unsafe.Pointer(sensitiveHeader.Data) - } else { - _p0 = unsafe.Pointer(&_zero) - } - _, _, e1 := unix.Syscall(unix.SYS_MLOCK2, uintptr(_p0), uintptr(sensitiveLen), 0) - if e1 != 0 { - switch e1 { - case 0: - err = nil - case unix.EAGAIN: - err = unix.EAGAIN - case unix.EINVAL: - err = unix.EINVAL - case unix.ENOENT: - err = unix.ENOENT - } - } - return err -} - -func MunlockAll(logger *log.Logger) error { - return unix.Munlockall() -} +//go:build dragonfly || freebsd || linux || openbsd || solaris +// +build dragonfly freebsd linux openbsd solaris + +package mlock + +import ( + "log" + "reflect" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +// Mlock - provides locking hook for OS's that support mlock +func Mlock(logger *log.Logger) error { + return unix.Mlockall(syscall.MCL_CURRENT | syscall.MCL_FUTURE) +} + +var _zero uintptr + +func Mlock2(logger *log.Logger, sensitive *string) error { + var _p0 unsafe.Pointer + var err error = nil + sensitiveHeader := (*reflect.StringHeader)(unsafe.Pointer(sensitive)) + sensitiveLen := sensitiveHeader.Len + if sensitiveLen > 0 { + _p0 = unsafe.Pointer(sensitiveHeader.Data) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := unix.Syscall(unix.SYS_MLOCK2, uintptr(_p0), uintptr(sensitiveLen), 0) + if e1 != 0 { + switch e1 { + case 0: + err = nil + case unix.EAGAIN: + err = unix.EAGAIN + case unix.EINVAL: + err = unix.EINVAL + case unix.ENOENT: + err = unix.ENOENT + } + } + return err +} + +func MunlockAll(logger *log.Logger) error { + return unix.Munlockall() +} diff --git a/pkg/utils/versionUtil.go b/pkg/utils/versionUtil.go index 6930cfe7a..2d6171e87 100644 --- a/pkg/utils/versionUtil.go +++ b/pkg/utils/versionUtil.go @@ -1,229 +1,229 @@ -package utils - -import ( - "fmt" - "sort" - "strconv" - "strings" - - "github.com/trimble-oss/tierceron/buildopts/coreopts" - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" -) - -func SplitEnv(env string) []string { - envVersion := make([]string, 2) - lastIndex := strings.LastIndex(env, "_") - if lastIndex == -1 { - envVersion[0] = env - envVersion[1] = "0" - } else { - envVersion[0] = env[0:lastIndex] - envVersion[1] = env[lastIndex+1:] - } - - return envVersion -} - -func GetEnvBasis(env string) string { - if strings.HasPrefix(env, "dev") { - return "dev" - } else if strings.HasPrefix(env, "QA") { - return "QA" - } else if strings.HasPrefix(env, "RQA") { - return "RQA" - } else if strings.HasPrefix(env, "staging") { - return "staging" - } else if strings.HasPrefix(env, "prod") { - return "prod" - } else { - return strings.Split(env, "_")[0] - } -} - -func GetProjectVersionInfo(driverConfig *DriverConfig, mod *helperkv.Modifier) map[string]map[string]interface{} { - versionMetadataMap := make(map[string]map[string]interface{}) - mod.VersionFilter = driverConfig.VersionFilter - var secretMetadataMap map[string]map[string]interface{} - var err error - mod.SectionKey = driverConfig.SectionKey - mod.SubSectionName = driverConfig.SubSectionName - mod.EnvBasis = strings.Split(driverConfig.EnvBasis, ".")[0] - if !driverConfig.CoreConfig.WantCerts { - secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "super-secrets", driverConfig.CoreConfig.Log) - if secretMetadataMap == nil { - secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "values", driverConfig.CoreConfig.Log) - } - } else { - //Certs are in values, not super secrets - secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "values", driverConfig.CoreConfig.Log) - } - var foundKey string - for key, value := range secretMetadataMap { - foundService := false - for _, service := range mod.VersionFilter { - if strings.HasSuffix(key, service) { - foundService = true - foundKey = key - break - } else if mod.SectionKey == "/Index/" && mod.SubSectionName != "" && strings.HasPrefix(key, "super-secrets"+mod.SectionKey+service+"/") { - foundService = true - foundKey = key - break - } - } - if foundService && len(foundKey) > 0 { - versionMetadataMap[foundKey] = value - } - } - - if err != nil { - fmt.Println("No version data available for this env") - LogErrorObject(&driverConfig.CoreConfig, err, false) - } - if len(versionMetadataMap) == 0 { - fmt.Println("No version data available for this env") - LogErrorObject(&driverConfig.CoreConfig, err, false) - } - - return versionMetadataMap -} - -func GetProjectVersions(driverConfig *DriverConfig, versionMetadataMap map[string]map[string]interface{}) []int { - var versionNumbers []int - for valuePath, data := range versionMetadataMap { - if len(driverConfig.ServiceFilter) > 0 { - found := false - for _, index := range driverConfig.ServiceFilter { - if strings.Contains(valuePath, index) { - found = true - } - } - if !found { - continue - } - } - projectFound := false - for _, project := range driverConfig.VersionFilter { - if strings.Contains(valuePath, project) { - projectFound = true - for key := range data { - versionNo, err := strconv.Atoi(key) - if err != nil { - fmt.Printf("Could not convert %s into a int", key) - } - versionNumbers = append(versionNumbers, versionNo) - } - } - if !projectFound { - continue - } - } - } - - sort.Ints(versionNumbers) - return versionNumbers -} - -func BoundCheck(driverConfig *DriverConfig, versionNumbers []int, version string) { - Cyan := "\033[36m" - Reset := "\033[0m" - if IsWindows() { - Reset = "" - Cyan = "" - } - if len(versionNumbers) >= 1 { - latestVersion := versionNumbers[len(versionNumbers)-1] - oldestVersion := versionNumbers[0] - userVersion, _ := strconv.Atoi(version) - if userVersion > latestVersion || userVersion < oldestVersion && len(versionNumbers) != 1 { - LogAndSafeExit(&driverConfig.CoreConfig, Cyan+"This version "+driverConfig.Env+" is not available as the latest version is "+strconv.Itoa(versionNumbers[len(versionNumbers)-1])+" and oldest version available is "+strconv.Itoa(versionNumbers[0])+Reset, 1) - } - } else { - LogAndSafeExit(&driverConfig.CoreConfig, Cyan+"No version data found"+Reset, 1) - } -} - -func GetProjectServices(driverConfig *DriverConfig, templateFiles []string) ([]string, []string, []string) { - projects := []string{} - services := []string{} - templateFilesContents := []string{} - - for _, templateFile := range templateFiles { - project, service, _, templateFileContent := GetProjectService(driverConfig, templateFile) - - projects = append(projects, project) - services = append(services, service) - templateFilesContents = append(templateFilesContents, templateFileContent) - } - - return projects, services, templateFilesContents -} - -// GetProjectService - returns project, service, and path to template on filesystem. -// driverConfig - driver configuration -// templateFile - full path to template file -// returns project, service, templatePath -func GetProjectService(driverConfig *DriverConfig, templateFile string) (string, string, int, string) { - templateFile = strings.ReplaceAll(strings.ReplaceAll(templateFile, "\\\\", "/"), "\\", "/") - splitDir := strings.Split(templateFile, "/") - var project, service string - offsetBase := 0 - var startDir []string = nil - if driverConfig != nil { - startDir = driverConfig.StartDir - } - - trcTemplateParam := coreopts.BuildOptions.GetFolderPrefix(startDir) + "_templates" - - for i, component := range splitDir { - if component == trcTemplateParam { - offsetBase = i - break - } - } - - project = splitDir[offsetBase+1] - var serviceIndex int - if len(project) == 0 && len(driverConfig.DeploymentConfig) > 0 { - if projectService, ok := driverConfig.DeploymentConfig["trcprojectservice"]; ok { - projectServiceParts := strings.Split(projectService.(string), "/") - project = projectServiceParts[0] - service = projectServiceParts[1] - serviceIndex = 0 - } - } else { - serviceIndex = offsetBase + 2 - service = splitDir[serviceIndex] - - // Clean up service naming (Everything after '.' removed) - if !strings.Contains(templateFile, "Common") { - dotIndex := strings.Index(service, ".") - if dotIndex > 0 && dotIndex <= len(service) { - service = service[0:dotIndex] - } - } else if strings.Contains(service, ".mf.tmpl") { - service = strings.Split(service, ".mf.tmpl")[0] - } - } - - return project, service, serviceIndex, templateFile -} - -func GetTemplateFileName(templateFile string, service string) string { - templateSplit := strings.Split(templateFile, service+"/") - templateFileName := strings.Split(templateSplit[len(templateSplit)-1], ".")[0] - - return templateFileName -} - -func RemoveDuplicates(versionFilter []string) []string { - keys := make(map[string]bool) //Removes any duplicates - cleanedFilter := []string{} - for _, entry := range versionFilter { - if _, value := keys[entry]; !value { - keys[entry] = true - cleanedFilter = append(cleanedFilter, entry) - } - } - return cleanedFilter -} +package utils + +import ( + "fmt" + "sort" + "strconv" + "strings" + + "github.com/trimble-oss/tierceron/buildopts/coreopts" + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" +) + +func SplitEnv(env string) []string { + envVersion := make([]string, 2) + lastIndex := strings.LastIndex(env, "_") + if lastIndex == -1 { + envVersion[0] = env + envVersion[1] = "0" + } else { + envVersion[0] = env[0:lastIndex] + envVersion[1] = env[lastIndex+1:] + } + + return envVersion +} + +func GetEnvBasis(env string) string { + if strings.HasPrefix(env, "dev") { + return "dev" + } else if strings.HasPrefix(env, "QA") { + return "QA" + } else if strings.HasPrefix(env, "RQA") { + return "RQA" + } else if strings.HasPrefix(env, "staging") { + return "staging" + } else if strings.HasPrefix(env, "prod") { + return "prod" + } else { + return strings.Split(env, "_")[0] + } +} + +func GetProjectVersionInfo(driverConfig *DriverConfig, mod *helperkv.Modifier) map[string]map[string]interface{} { + versionMetadataMap := make(map[string]map[string]interface{}) + mod.VersionFilter = driverConfig.VersionFilter + var secretMetadataMap map[string]map[string]interface{} + var err error + mod.SectionKey = driverConfig.SectionKey + mod.SubSectionName = driverConfig.SubSectionName + mod.EnvBasis = strings.Split(driverConfig.EnvBasis, ".")[0] + if !driverConfig.CoreConfig.WantCerts { + secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "super-secrets", driverConfig.CoreConfig.Log) + if secretMetadataMap == nil { + secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "values", driverConfig.CoreConfig.Log) + } + } else { + //Certs are in values, not super secrets + secretMetadataMap, err = mod.GetVersionValues(mod, driverConfig.CoreConfig.WantCerts, "values", driverConfig.CoreConfig.Log) + } + var foundKey string + for key, value := range secretMetadataMap { + foundService := false + for _, service := range mod.VersionFilter { + if strings.HasSuffix(key, service) { + foundService = true + foundKey = key + break + } else if mod.SectionKey == "/Index/" && mod.SubSectionName != "" && strings.HasPrefix(key, "super-secrets"+mod.SectionKey+service+"/") { + foundService = true + foundKey = key + break + } + } + if foundService && len(foundKey) > 0 { + versionMetadataMap[foundKey] = value + } + } + + if err != nil { + fmt.Println("No version data available for this env") + LogErrorObject(&driverConfig.CoreConfig, err, false) + } + if len(versionMetadataMap) == 0 { + fmt.Println("No version data available for this env") + LogErrorObject(&driverConfig.CoreConfig, err, false) + } + + return versionMetadataMap +} + +func GetProjectVersions(driverConfig *DriverConfig, versionMetadataMap map[string]map[string]interface{}) []int { + var versionNumbers []int + for valuePath, data := range versionMetadataMap { + if len(driverConfig.ServiceFilter) > 0 { + found := false + for _, index := range driverConfig.ServiceFilter { + if strings.Contains(valuePath, index) { + found = true + } + } + if !found { + continue + } + } + projectFound := false + for _, project := range driverConfig.VersionFilter { + if strings.Contains(valuePath, project) { + projectFound = true + for key := range data { + versionNo, err := strconv.Atoi(key) + if err != nil { + fmt.Printf("Could not convert %s into a int", key) + } + versionNumbers = append(versionNumbers, versionNo) + } + } + if !projectFound { + continue + } + } + } + + sort.Ints(versionNumbers) + return versionNumbers +} + +func BoundCheck(driverConfig *DriverConfig, versionNumbers []int, version string) { + Cyan := "\033[36m" + Reset := "\033[0m" + if IsWindows() { + Reset = "" + Cyan = "" + } + if len(versionNumbers) >= 1 { + latestVersion := versionNumbers[len(versionNumbers)-1] + oldestVersion := versionNumbers[0] + userVersion, _ := strconv.Atoi(version) + if userVersion > latestVersion || userVersion < oldestVersion && len(versionNumbers) != 1 { + LogAndSafeExit(&driverConfig.CoreConfig, Cyan+"This version "+driverConfig.Env+" is not available as the latest version is "+strconv.Itoa(versionNumbers[len(versionNumbers)-1])+" and oldest version available is "+strconv.Itoa(versionNumbers[0])+Reset, 1) + } + } else { + LogAndSafeExit(&driverConfig.CoreConfig, Cyan+"No version data found"+Reset, 1) + } +} + +func GetProjectServices(driverConfig *DriverConfig, templateFiles []string) ([]string, []string, []string) { + projects := []string{} + services := []string{} + templateFilesContents := []string{} + + for _, templateFile := range templateFiles { + project, service, _, templateFileContent := GetProjectService(driverConfig, templateFile) + + projects = append(projects, project) + services = append(services, service) + templateFilesContents = append(templateFilesContents, templateFileContent) + } + + return projects, services, templateFilesContents +} + +// GetProjectService - returns project, service, and path to template on filesystem. +// driverConfig - driver configuration +// templateFile - full path to template file +// returns project, service, templatePath +func GetProjectService(driverConfig *DriverConfig, templateFile string) (string, string, int, string) { + templateFile = strings.ReplaceAll(strings.ReplaceAll(templateFile, "\\\\", "/"), "\\", "/") + splitDir := strings.Split(templateFile, "/") + var project, service string + offsetBase := 0 + var startDir []string = nil + if driverConfig != nil { + startDir = driverConfig.StartDir + } + + trcTemplateParam := coreopts.BuildOptions.GetFolderPrefix(startDir) + "_templates" + + for i, component := range splitDir { + if component == trcTemplateParam { + offsetBase = i + break + } + } + + project = splitDir[offsetBase+1] + var serviceIndex int + if len(project) == 0 && len(driverConfig.DeploymentConfig) > 0 { + if projectService, ok := driverConfig.DeploymentConfig["trcprojectservice"]; ok { + projectServiceParts := strings.Split(projectService.(string), "/") + project = projectServiceParts[0] + service = projectServiceParts[1] + serviceIndex = 0 + } + } else { + serviceIndex = offsetBase + 2 + service = splitDir[serviceIndex] + + // Clean up service naming (Everything after '.' removed) + if !strings.Contains(templateFile, "Common") { + dotIndex := strings.Index(service, ".") + if dotIndex > 0 && dotIndex <= len(service) { + service = service[0:dotIndex] + } + } else if strings.Contains(service, ".mf.tmpl") { + service = strings.Split(service, ".mf.tmpl")[0] + } + } + + return project, service, serviceIndex, templateFile +} + +func GetTemplateFileName(templateFile string, service string) string { + templateSplit := strings.Split(templateFile, service+"/") + templateFileName := strings.Split(templateSplit[len(templateSplit)-1], ".")[0] + + return templateFileName +} + +func RemoveDuplicates(versionFilter []string) []string { + keys := make(map[string]bool) //Removes any duplicates + cleanedFilter := []string{} + for _, entry := range versionFilter { + if _, value := keys[entry]; !value { + keys[entry] = true + cleanedFilter = append(cleanedFilter, entry) + } + } + return cleanedFilter +} diff --git a/pkg/validator/keystore.go b/pkg/validator/keystore.go index a1486bda0..42f631920 100644 --- a/pkg/validator/keystore.go +++ b/pkg/validator/keystore.go @@ -1,161 +1,161 @@ -package validator - -import ( - "bufio" - "bytes" - "crypto/rsa" - "crypto/x509" - "encoding/asn1" - "encoding/pem" - "errors" - "fmt" - "os" - "strings" - "time" - - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - - "github.com/pavlo-v-chernykh/keystore-go/v4" - - "github.com/youmark/pkcs8" - pkcs "golang.org/x/crypto/pkcs12" - "golang.org/x/crypto/ssh" -) - -// Copied from pkcs12.go... why can't they just make these public. Gr... -// PEM block types -const ( - certificateType = "CERTIFICATE" - privateKeyType = "PRIVATE KEY" - rsaPrivateKeyType = "RSA PRIVATE KEY" -) - -func StoreKeystore(driverConfig *eUtils.DriverConfig, trustStorePassword string) ([]byte, error) { - buffer := &bytes.Buffer{} - keystoreWriter := bufio.NewWriter(buffer) - - if driverConfig.KeyStore == nil { - return nil, errors.New("cert bundle not properly named") - } - storeErr := driverConfig.KeyStore.Store(keystoreWriter, []byte(trustStorePassword)) - if storeErr != nil { - return nil, storeErr - } - keystoreWriter.Flush() - - return buffer.Bytes(), nil -} - -func AddToKeystore(driverConfig *eUtils.DriverConfig, alias string, password []byte, certBundleJks string, data []byte) error { - // TODO: Add support for this format? golang.org/x/crypto/pkcs12 - - if !strings.HasSuffix(driverConfig.WantKeystore, ".jks") && strings.HasSuffix(certBundleJks, ".jks") { - driverConfig.WantKeystore = certBundleJks - } - - if driverConfig.KeyStore == nil { - fmt.Println("Making new keystore.") - ks := keystore.New() - driverConfig.KeyStore = &ks - } - - block, _ := pem.Decode(data) - if block == nil { - key, cert, err := pkcs.Decode(data, string(password)) // Note the order of the return values. - if err != nil { - return err - } - pkcs8Key, err := pkcs8.ConvertPrivateKeyToPKCS8(key, password) - if err != nil { - return err - } - - driverConfig.KeyStore.SetPrivateKeyEntry(alias, keystore.PrivateKeyEntry{ - CreationTime: time.Now(), - PrivateKey: pkcs8Key, - CertificateChain: []keystore.Certificate{ - { - Type: "X509", - Content: cert.Raw, - }, - }, - }, password) - - } else { - if block.Type == certificateType { - aliasCommon := strings.Replace(alias, "cert.pem", "", 1) - driverConfig.KeyStore.SetTrustedCertificateEntry(aliasCommon, keystore.TrustedCertificateEntry{ - CreationTime: time.Now(), - Certificate: keystore.Certificate{ - Type: "X509", - Content: block.Bytes, - }, - }) - return nil - } - privateKeyBytes, err := ssh.ParseRawPrivateKey(data) - if err == nil { - privateKeyBytes, err := pkcs8.MarshalPrivateKey(privateKeyBytes, []byte{}, nil) - if err != nil { - return err - } - aliasCommon := strings.Replace(alias, "key.pem", "", 1) - - driverConfig.KeyStore.SetPrivateKeyEntry(aliasCommon, keystore.PrivateKeyEntry{ - CreationTime: time.Now(), - PrivateKey: privateKeyBytes, - }, password) - } else { - return err - } - } - - return nil -} - -// ValidateKeyStore validates the sendgrid API key. -func ValidateKeyStore(config *core.CoreConfig, filename string, pass string) (bool, error) { - file, err := os.ReadFile(filename) - if err != nil { - return false, err - } - pemBlocks, errToPEM := pkcs.ToPEM(file, pass) - if errToPEM != nil { - return false, errors.New("failed to parse: " + err.Error()) - } - isValid := false - - for _, pemBlock := range pemBlocks { - // PEM constancts defined but not exposed in - // certificateType = "CERTIFICATE" - // privateKeyType = "PRIVATE KEY" - - switch (*pemBlock).Type { - case certificateType: - var cert x509.Certificate - _, errUnmarshal := asn1.Unmarshal((*pemBlock).Bytes, &cert) - if errUnmarshal != nil { - return false, errors.New("failed to parse: " + err.Error()) - } - - isCertValid, err := VerifyCertificate(&cert, "") - if err != nil { - eUtils.LogInfo(config, "Certificate validation failure.") - } - isValid = isCertValid - case privateKeyType: - var key rsa.PrivateKey - _, errUnmarshal := asn1.Unmarshal((*pemBlock).Bytes, &key) - if errUnmarshal != nil { - return false, errors.New("failed to parse: " + err.Error()) - } - - if err := key.Validate(); err != nil { - eUtils.LogInfo(config, "key validation didn't work") - } - } - } - - return isValid, err -} +package validator + +import ( + "bufio" + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + "os" + "strings" + "time" + + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + + "github.com/pavlo-v-chernykh/keystore-go/v4" + + "github.com/youmark/pkcs8" + pkcs "golang.org/x/crypto/pkcs12" + "golang.org/x/crypto/ssh" +) + +// Copied from pkcs12.go... why can't they just make these public. Gr... +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" + rsaPrivateKeyType = "RSA PRIVATE KEY" +) + +func StoreKeystore(driverConfig *eUtils.DriverConfig, trustStorePassword string) ([]byte, error) { + buffer := &bytes.Buffer{} + keystoreWriter := bufio.NewWriter(buffer) + + if driverConfig.KeyStore == nil { + return nil, errors.New("cert bundle not properly named") + } + storeErr := driverConfig.KeyStore.Store(keystoreWriter, []byte(trustStorePassword)) + if storeErr != nil { + return nil, storeErr + } + keystoreWriter.Flush() + + return buffer.Bytes(), nil +} + +func AddToKeystore(driverConfig *eUtils.DriverConfig, alias string, password []byte, certBundleJks string, data []byte) error { + // TODO: Add support for this format? golang.org/x/crypto/pkcs12 + + if !strings.HasSuffix(driverConfig.WantKeystore, ".jks") && strings.HasSuffix(certBundleJks, ".jks") { + driverConfig.WantKeystore = certBundleJks + } + + if driverConfig.KeyStore == nil { + fmt.Println("Making new keystore.") + ks := keystore.New() + driverConfig.KeyStore = &ks + } + + block, _ := pem.Decode(data) + if block == nil { + key, cert, err := pkcs.Decode(data, string(password)) // Note the order of the return values. + if err != nil { + return err + } + pkcs8Key, err := pkcs8.ConvertPrivateKeyToPKCS8(key, password) + if err != nil { + return err + } + + driverConfig.KeyStore.SetPrivateKeyEntry(alias, keystore.PrivateKeyEntry{ + CreationTime: time.Now(), + PrivateKey: pkcs8Key, + CertificateChain: []keystore.Certificate{ + { + Type: "X509", + Content: cert.Raw, + }, + }, + }, password) + + } else { + if block.Type == certificateType { + aliasCommon := strings.Replace(alias, "cert.pem", "", 1) + driverConfig.KeyStore.SetTrustedCertificateEntry(aliasCommon, keystore.TrustedCertificateEntry{ + CreationTime: time.Now(), + Certificate: keystore.Certificate{ + Type: "X509", + Content: block.Bytes, + }, + }) + return nil + } + privateKeyBytes, err := ssh.ParseRawPrivateKey(data) + if err == nil { + privateKeyBytes, err := pkcs8.MarshalPrivateKey(privateKeyBytes, []byte{}, nil) + if err != nil { + return err + } + aliasCommon := strings.Replace(alias, "key.pem", "", 1) + + driverConfig.KeyStore.SetPrivateKeyEntry(aliasCommon, keystore.PrivateKeyEntry{ + CreationTime: time.Now(), + PrivateKey: privateKeyBytes, + }, password) + } else { + return err + } + } + + return nil +} + +// ValidateKeyStore validates the sendgrid API key. +func ValidateKeyStore(config *core.CoreConfig, filename string, pass string) (bool, error) { + file, err := os.ReadFile(filename) + if err != nil { + return false, err + } + pemBlocks, errToPEM := pkcs.ToPEM(file, pass) + if errToPEM != nil { + return false, errors.New("failed to parse: " + err.Error()) + } + isValid := false + + for _, pemBlock := range pemBlocks { + // PEM constancts defined but not exposed in + // certificateType = "CERTIFICATE" + // privateKeyType = "PRIVATE KEY" + + switch (*pemBlock).Type { + case certificateType: + var cert x509.Certificate + _, errUnmarshal := asn1.Unmarshal((*pemBlock).Bytes, &cert) + if errUnmarshal != nil { + return false, errors.New("failed to parse: " + err.Error()) + } + + isCertValid, err := VerifyCertificate(&cert, "") + if err != nil { + eUtils.LogInfo(config, "Certificate validation failure.") + } + isValid = isCertValid + case privateKeyType: + var key rsa.PrivateKey + _, errUnmarshal := asn1.Unmarshal((*pemBlock).Bytes, &key) + if errUnmarshal != nil { + return false, errors.New("failed to parse: " + err.Error()) + } + + if err := key.Validate(); err != nil { + eUtils.LogInfo(config, "key validation didn't work") + } + } + } + + return isValid, err +} diff --git a/pkg/vaulthelper/kv/cert.go b/pkg/vaulthelper/kv/cert.go index 219edd694..3d350694f 100644 --- a/pkg/vaulthelper/kv/cert.go +++ b/pkg/vaulthelper/kv/cert.go @@ -159,7 +159,7 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "../../certs/cert_files/dcipublic.pem": CertsCert_filesDcipublicPem, + "../../certs/cert_files/dcipublic.pem": CertsCert_filesDcipublicPem, "../../certs/cert_files/dcidevpublic.pem": CertsCert_filesDcidevpublicPem, } @@ -167,11 +167,13 @@ var _bindata = map[string]func() (*asset, error){ // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the // following hierarchy: -// data/ -// foo.txt -// img/ -// a.png -// b.png +// +// data/ +// foo.txt +// img/ +// a.png +// b.png +// // then AssetDir("data") would return []string{"foo.txt", "img"} // AssetDir("data/img") would return []string{"a.png", "b.png"} // AssetDir("foo.txt") and AssetDir("notexist") would return an error @@ -202,13 +204,14 @@ type bintree struct { Func func() (*asset, error) Children map[string]*bintree } + var _bintree = &bintree{nil, map[string]*bintree{ - "..": &bintree{nil, map[string]*bintree{ - "..": &bintree{nil, map[string]*bintree{ - "certs": &bintree{nil, map[string]*bintree{ - "cert_files": &bintree{nil, map[string]*bintree{ - "dcidevpublic.pem": &bintree{CertsCert_filesDcidevpublicPem, map[string]*bintree{}}, - "dcipublic.pem": &bintree{CertsCert_filesDcipublicPem, map[string]*bintree{}}, + "..": {nil, map[string]*bintree{ + "..": {nil, map[string]*bintree{ + "certs": {nil, map[string]*bintree{ + "cert_files": {nil, map[string]*bintree{ + "dcidevpublic.pem": {CertsCert_filesDcidevpublicPem, map[string]*bintree{}}, + "dcipublic.pem": {CertsCert_filesDcipublicPem, map[string]*bintree{}}, }}, }}, }}, @@ -261,4 +264,3 @@ func _filePath(dir, name string) string { cannonicalName := strings.Replace(name, "\\", "/", -1) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) } - diff --git a/pkg/vaulthelper/system/AppRole.go b/pkg/vaulthelper/system/AppRole.go index ad0292892..8ae0816b6 100644 --- a/pkg/vaulthelper/system/AppRole.go +++ b/pkg/vaulthelper/system/AppRole.go @@ -1,219 +1,219 @@ -package system - -import ( - "encoding/json" - "fmt" - - "github.com/hashicorp/vault/api" -) - -// NewRoleOptions is used to create a new approle -type NewRoleOptions struct { - BindSecretID bool `json:"bind_secret_id,omitempty"` - SecretIDBoundCIDRs []string `json:"secret_id_bound_cidrs,omitempty"` - TokenBoundCIDRs []string `json:"token_bound_cidrs,omitempty"` - Policies []string `json:"policies"` - SecretIDTTL string `json:"secret_id_num_uses,omitempty"` - TokenNumUses int `json:"token_num_uses,omitempty"` - TokenTTL string `json:"token_ttl,omitempty"` - TokenMaxTTL string `json:"token_max_ttl,omitempty"` - Period string `json:"period,omitempty"` - EnableLocalSecretIDs string `json:"enable_local_secret_ids,omitempty"` -} - -// YamlNewTokenRoleOptions is used to create a new approle -type YamlNewTokenRoleOptions struct { - RoleName string `yaml:"role_name,omitempty"` - TokenBoundCIDRs []string `yaml:"token_bound_cidrs,omitempty"` -} - -// NewTokenRoleOptions is used to create a new approle -type NewTokenRoleOptions struct { - RoleName string `json:"role_name,omitempty"` - AllowedPolicies []string `json:"allowed_policies,omitempty"` - DisallowedPolicies []string `json:"disallowed_policies,omitempty"` - Orphan bool `json:"orphan,omitempty"` - Renewable bool `json:"renewable,omitempty"` - PathSuffix string `json:"path_suffix,omitempty"` - AllowedEntityAliases []string `json:"allowed_entity_aliases,omitempty"` - TokenBoundCIDRs []string `json:"token_bound_cidrs,omitempty"` - TokenExplicitMaxTTL int `json:"token_explicit_max_ttl,omitempty"` - TokenNoDefaultPolicy bool `json:"token_no_default_policy,omitempty"` - TokenNumUses int `json:"token_num_uses,omitempty"` - TokenPeriod int `json:"token_period,omitempty"` - TokenType string `json:"token_type,omitempty"` -} - -// EnableAppRole enables the app role auth method and returns any errors -func (v *Vault) EnableAppRole() error { - sys := v.client.Sys() - err := sys.EnableAuthWithOptions("approle", &api.EnableAuthOptions{ - Type: "approle", - Description: "Auth endpoint for vault config", - Config: api.AuthConfigInput{ - DefaultLeaseTTL: "10m", - MaxLeaseTTL: "15m", - }, - }) - return err -} - -// CreateNewRole creates a new role with given options -func (v *Vault) CreateNewRole(roleName string, options *NewRoleOptions) error { - r := v.client.NewRequest("POST", fmt.Sprintf("/v1/auth/approle/role/%s", roleName)) - if err := r.SetJSONBody(options); err != nil { - return err - } - - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - return err -} - -// DeleteRole deletes role with given role name -func (v *Vault) DeleteRole(roleName string) (*api.Response, error) { - r := v.client.NewRequest("DELETE", fmt.Sprintf("/v1/auth/approle/role/%s", roleName)) - - response, err := v.client.RawRequest(r) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - return response, err -} - -// CreateNewTokenCidrRole creates a new token cidr only role with given cidr options. -func (v *Vault) CreateNewTokenCidrRole(options *YamlNewTokenRoleOptions) error { - rolePath := fmt.Sprintf("/v1/auth/token/roles/%s", options.RoleName) - r := v.client.NewRequest("POST", rolePath) - limitedTokenRole := NewTokenRoleOptions{} - limitedTokenRole.TokenBoundCIDRs = options.TokenBoundCIDRs - - if err := r.SetJSONBody(options); err != nil { - return err - } - - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - return err -} - -// GetRoleID checks for the given role name and returns the coresponding id if it exists -func (v *Vault) GetRoleID(roleName string) (string, string, error) { - r := v.client.NewRequest("GET", fmt.Sprintf("/v1/auth/approle/role/%s/role-id", roleName)) - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - return "", "", err - } - - var jsonData map[string]interface{} - if err = response.DecodeJSON(&jsonData); err != nil { - return "", "", err - } - - if raw, ok := jsonData["data"].(map[string]interface{}); ok { - if roleID, ok := raw["role_id"].(string); ok { - return roleID, string(jsonData["lease_duration"].(json.Number)), nil - } - return "", "", fmt.Errorf("error parsing response for key 'data.id'") - } - - return "", "", fmt.Errorf("error parsing resonse for key 'data'") -} - -// GetSecretID checks the vault for the secret ID corresponding to the role name -func (v *Vault) GetSecretID(roleName string) (string, error) { - r := v.client.NewRequest("POST", fmt.Sprintf("/v1/auth/approle/role/%s/secret-id", roleName)) - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - return "", err - } - - var jsonData map[string]interface{} - if err = response.DecodeJSON(&jsonData); err != nil { - return "", err - } - - if raw, ok := jsonData["data"].(map[string]interface{}); ok { - if secretID, ok := raw["secret_id"].(string); ok { - return secretID, nil - } - return "", fmt.Errorf("error parsing response for key 'data.secret_id'") - } - - return "", fmt.Errorf("error parsing resonse for key 'data'") -} - -// GetListApproles lists available approles -func (v *Vault) GetListApproles() (string, error) { - r := v.client.NewRequest("LIST", "/v1/auth/approle/role") - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - return "", err - } - - var jsonData map[string]interface{} - if err = response.DecodeJSON(&jsonData); err != nil { - return "", err - } - - return "", fmt.Errorf("error parsing resonse for key 'data'") -} - -// AppRoleLogin tries logging into the vault using app role and returns a client token on success -func (v *Vault) AppRoleLogin(roleID string, secretID string) (string, error) { - r := v.client.NewRequest("POST", "/v1/auth/approle/login") - - payload := map[string]interface{}{ - "role_id": roleID, - "secret_id": secretID, - } - - if err := r.SetJSONBody(payload); err != nil { - return "", err - } - - response, err := v.client.RawRequest(r) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - return "", err - } - - var jsonData map[string]interface{} - - if err = response.DecodeJSON(&jsonData); err != nil { - return "", err - } - - if authData, ok := jsonData["auth"].(map[string]interface{}); ok { - if token, ok := authData["client_token"].(string); ok { - return token, nil - } - return "", fmt.Errorf("error parsing response for key 'auth.client_token'") - } - - return "", fmt.Errorf("error parsing response for key 'auth'") -} +package system + +import ( + "encoding/json" + "fmt" + + "github.com/hashicorp/vault/api" +) + +// NewRoleOptions is used to create a new approle +type NewRoleOptions struct { + BindSecretID bool `json:"bind_secret_id,omitempty"` + SecretIDBoundCIDRs []string `json:"secret_id_bound_cidrs,omitempty"` + TokenBoundCIDRs []string `json:"token_bound_cidrs,omitempty"` + Policies []string `json:"policies"` + SecretIDTTL string `json:"secret_id_num_uses,omitempty"` + TokenNumUses int `json:"token_num_uses,omitempty"` + TokenTTL string `json:"token_ttl,omitempty"` + TokenMaxTTL string `json:"token_max_ttl,omitempty"` + Period string `json:"period,omitempty"` + EnableLocalSecretIDs string `json:"enable_local_secret_ids,omitempty"` +} + +// YamlNewTokenRoleOptions is used to create a new approle +type YamlNewTokenRoleOptions struct { + RoleName string `yaml:"role_name,omitempty"` + TokenBoundCIDRs []string `yaml:"token_bound_cidrs,omitempty"` +} + +// NewTokenRoleOptions is used to create a new approle +type NewTokenRoleOptions struct { + RoleName string `json:"role_name,omitempty"` + AllowedPolicies []string `json:"allowed_policies,omitempty"` + DisallowedPolicies []string `json:"disallowed_policies,omitempty"` + Orphan bool `json:"orphan,omitempty"` + Renewable bool `json:"renewable,omitempty"` + PathSuffix string `json:"path_suffix,omitempty"` + AllowedEntityAliases []string `json:"allowed_entity_aliases,omitempty"` + TokenBoundCIDRs []string `json:"token_bound_cidrs,omitempty"` + TokenExplicitMaxTTL int `json:"token_explicit_max_ttl,omitempty"` + TokenNoDefaultPolicy bool `json:"token_no_default_policy,omitempty"` + TokenNumUses int `json:"token_num_uses,omitempty"` + TokenPeriod int `json:"token_period,omitempty"` + TokenType string `json:"token_type,omitempty"` +} + +// EnableAppRole enables the app role auth method and returns any errors +func (v *Vault) EnableAppRole() error { + sys := v.client.Sys() + err := sys.EnableAuthWithOptions("approle", &api.EnableAuthOptions{ + Type: "approle", + Description: "Auth endpoint for vault config", + Config: api.AuthConfigInput{ + DefaultLeaseTTL: "10m", + MaxLeaseTTL: "15m", + }, + }) + return err +} + +// CreateNewRole creates a new role with given options +func (v *Vault) CreateNewRole(roleName string, options *NewRoleOptions) error { + r := v.client.NewRequest("POST", fmt.Sprintf("/v1/auth/approle/role/%s", roleName)) + if err := r.SetJSONBody(options); err != nil { + return err + } + + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + return err +} + +// DeleteRole deletes role with given role name +func (v *Vault) DeleteRole(roleName string) (*api.Response, error) { + r := v.client.NewRequest("DELETE", fmt.Sprintf("/v1/auth/approle/role/%s", roleName)) + + response, err := v.client.RawRequest(r) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + return response, err +} + +// CreateNewTokenCidrRole creates a new token cidr only role with given cidr options. +func (v *Vault) CreateNewTokenCidrRole(options *YamlNewTokenRoleOptions) error { + rolePath := fmt.Sprintf("/v1/auth/token/roles/%s", options.RoleName) + r := v.client.NewRequest("POST", rolePath) + limitedTokenRole := NewTokenRoleOptions{} + limitedTokenRole.TokenBoundCIDRs = options.TokenBoundCIDRs + + if err := r.SetJSONBody(options); err != nil { + return err + } + + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + return err +} + +// GetRoleID checks for the given role name and returns the coresponding id if it exists +func (v *Vault) GetRoleID(roleName string) (string, string, error) { + r := v.client.NewRequest("GET", fmt.Sprintf("/v1/auth/approle/role/%s/role-id", roleName)) + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + return "", "", err + } + + var jsonData map[string]interface{} + if err = response.DecodeJSON(&jsonData); err != nil { + return "", "", err + } + + if raw, ok := jsonData["data"].(map[string]interface{}); ok { + if roleID, ok := raw["role_id"].(string); ok { + return roleID, string(jsonData["lease_duration"].(json.Number)), nil + } + return "", "", fmt.Errorf("error parsing response for key 'data.id'") + } + + return "", "", fmt.Errorf("error parsing resonse for key 'data'") +} + +// GetSecretID checks the vault for the secret ID corresponding to the role name +func (v *Vault) GetSecretID(roleName string) (string, error) { + r := v.client.NewRequest("POST", fmt.Sprintf("/v1/auth/approle/role/%s/secret-id", roleName)) + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + return "", err + } + + var jsonData map[string]interface{} + if err = response.DecodeJSON(&jsonData); err != nil { + return "", err + } + + if raw, ok := jsonData["data"].(map[string]interface{}); ok { + if secretID, ok := raw["secret_id"].(string); ok { + return secretID, nil + } + return "", fmt.Errorf("error parsing response for key 'data.secret_id'") + } + + return "", fmt.Errorf("error parsing resonse for key 'data'") +} + +// GetListApproles lists available approles +func (v *Vault) GetListApproles() (string, error) { + r := v.client.NewRequest("LIST", "/v1/auth/approle/role") + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + return "", err + } + + var jsonData map[string]interface{} + if err = response.DecodeJSON(&jsonData); err != nil { + return "", err + } + + return "", fmt.Errorf("error parsing resonse for key 'data'") +} + +// AppRoleLogin tries logging into the vault using app role and returns a client token on success +func (v *Vault) AppRoleLogin(roleID string, secretID string) (string, error) { + r := v.client.NewRequest("POST", "/v1/auth/approle/login") + + payload := map[string]interface{}{ + "role_id": roleID, + "secret_id": secretID, + } + + if err := r.SetJSONBody(payload); err != nil { + return "", err + } + + response, err := v.client.RawRequest(r) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + return "", err + } + + var jsonData map[string]interface{} + + if err = response.DecodeJSON(&jsonData); err != nil { + return "", err + } + + if authData, ok := jsonData["auth"].(map[string]interface{}); ok { + if token, ok := authData["client_token"].(string); ok { + return token, nil + } + return "", fmt.Errorf("error parsing response for key 'auth.client_token'") + } + + return "", fmt.Errorf("error parsing response for key 'auth'") +} diff --git a/pkg/vaulthelper/system/Vault.go b/pkg/vaulthelper/system/Vault.go index eb696ca89..8b6233130 100644 --- a/pkg/vaulthelper/system/Vault.go +++ b/pkg/vaulthelper/system/Vault.go @@ -1,571 +1,571 @@ -package system - -import ( - "errors" - "fmt" - "io" - "log" - "net/http" - "os" - "strings" - "time" - - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - - "github.com/hashicorp/vault/api" - "gopkg.in/yaml.v2" -) - -// Vault Represents a vault connection for managing the vault's properties -type Vault struct { - httpClient *http.Client // Handle to http client. - client *api.Client // Client connected to vault - shards []string // Master key shards used to unseal vault -} - -// KeyTokenWrapper Contains the unseal keys and root token -type KeyTokenWrapper struct { - Keys []string // Base 64 encoded keys - Token string // Root token for the vault -} - -// NewVault Constructs a new vault at the given address with the given access token -func NewVault(insecure bool, address string, env string, newVault bool, pingVault bool, scanVault bool, logger *log.Logger) (*Vault, error) { - return NewVaultWithNonlocal(insecure, - address, - env, - newVault, - pingVault, - scanVault, - false, // allowNonLocal - false - logger) -} - -// NewVault Constructs a new vault at the given address with the given access token allowing insecure for non local. -func NewVaultWithNonlocal(insecure bool, address string, env string, newVault bool, pingVault bool, scanVault bool, allowNonLocal bool, logger *log.Logger) (*Vault, error) { - var httpClient *http.Client - var err error - - if allowNonLocal { - httpClient, err = helperkv.CreateHTTPClientAllowNonLocal(insecure, address, env, scanVault, true) - } else { - httpClient, err = helperkv.CreateHTTPClient(insecure, address, env, scanVault) - } - - if err != nil { - logger.Println("Connection to vault couldn't be made - vaultHost: " + address) - return nil, err - } - client, err := api.NewClient(&api.Config{Address: address, HttpClient: httpClient}) - if err != nil { - logger.Println("vaultHost: " + address) - return nil, err - } - - health, err := client.Sys().Health() - if err != nil { - return nil, err - } - - if pingVault { - fmt.Println("Ping success!") - logger.Println("Ping success!") - return nil, nil - } - - if !newVault && health.Sealed { - return nil, errors.New("Vault is sealed at " + address) - } - - return &Vault{ - client: client, - shards: nil}, err -} - -// Confirms we have a valid and active connection to vault. If it doesn't, it re-establishes a new connection. -func (v *Vault) RefreshClient() error { - tries := 0 - var refreshErr error - - for tries < 3 { - if _, err := v.GetStatus(); err != nil { - if v.httpClient != nil { - v.httpClient.CloseIdleConnections() - } - - client, err := api.NewClient(&api.Config{Address: v.client.Address(), HttpClient: v.client.CloneConfig().HttpClient}) - if err != nil { - refreshErr = err - } else { - v.client = client - return nil - } - - tries = tries + 1 - } else { - tries = 3 - } - } - - return refreshErr -} - -// SetToken Stores the access token for this vault -func (v *Vault) SetToken(token string) { - v.client.SetToken(token) -} - -// GetToken Fetches current token from client -func (v *Vault) GetToken() string { - return v.client.Token() -} - -// GetTokenInfo fetches data regarding this token -func (v *Vault) GetTokenInfo(tokenName string) (map[string]interface{}, error) { - token, err := v.client.Auth().Token().Lookup(tokenName) - if token == nil { - return nil, err - } - return token.Data, err -} - -// RevokeToken If proper access given, revokes access of a token and all children -func (v *Vault) RevokeToken(token string) error { - return v.client.Auth().Token().RevokeTree(token) -} - -// RevokeSelf Revokes token of current client -func (v *Vault) RevokeSelf() error { - return v.client.Auth().Token().RevokeSelf("") -} - -// RenewSelf Renews the token associated with this vault struct -func (v *Vault) RenewSelf(increment int) error { - _, err := v.client.Auth().Token().RenewSelf(increment) - return err -} - -// GetOrRevokeTokensInScope() -func (v *Vault) GetOrRevokeTokensInScope(dir string, tokenFilter string, tokenExpiration bool, logger *log.Logger) error { - var tokenPath = dir - var tokenPolicies = []string{} - - files, err := os.ReadDir(tokenPath) - if err != nil { - log.Fatal(err) - } - - for _, f := range files { - if f.IsDir() { - continue - } - if tokenFilter != "" && !strings.HasPrefix(f.Name(), tokenFilter) { - // Token doesn't match filter... Skipping. - continue - } - var file, err = os.OpenFile(tokenPath+string(os.PathSeparator)+f.Name(), os.O_RDWR, 0644) - if file != nil { - defer file.Close() - } - if err != nil { - log.Fatal(err) - } - - byteValue, _ := io.ReadAll(file) - token := api.TokenCreateRequest{} - yaml.Unmarshal(byteValue, &token) - tokenPolicies = append(tokenPolicies, token.Policies...) - } - r := v.client.NewRequest("LIST", "/v1/auth/token/accessors") - response, err := v.client.RawRequest(r) - - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - log.Fatal(err) - } - - var jsonData map[string]interface{} - - if err = response.DecodeJSON(&jsonData); err != nil { - return err - } - - if data, ok := jsonData["data"].(map[string]interface{}); ok { - if accessors, ok := data["keys"].([]interface{}); ok { - for _, accessor := range accessors { - b := v.client.NewRequest("POST", "/v1/auth/token/lookup-accessor") - - payload := map[string]interface{}{ - "accessor": accessor, - } - - if err := b.SetJSONBody(payload); err != nil { - return err - } - response, err := v.client.RawRequest(b) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - if response.StatusCode == 403 || response.StatusCode == 400 { - // Some accessors we don't have access to, but we don't care about those. - continue - } else { - log.Fatal(err) - return err - } - } - var accessorDataMap map[string]interface{} - if err = response.DecodeJSON(&accessorDataMap); err != nil { - return err - } - var expirationDate string - var expirationDateOk bool - var matchedPolicy string - if accessorData, ok := accessorDataMap["data"].(map[string]interface{}); ok { - if expirationDate, expirationDateOk = accessorData["expire_time"].(string); expirationDateOk { - currentTime := time.Now() - expirationTime, timeError := time.Parse(time.RFC3339Nano, expirationDate) - if timeError == nil && currentTime.Before(expirationTime) { - if policies, ok := accessorData["policies"].([]interface{}); ok { - for _, policy := range policies { - for _, tokenPolicy := range tokenPolicies { - if policy.(string) == "root" || strings.EqualFold(policy.(string), tokenPolicy) { - matchedPolicy = tokenPolicy - goto revokeAccessor - } - } - } - } - } - } - continue - revokeAccessor: - if tokenExpiration { - fmt.Println("Token with the policy " + matchedPolicy + " expires on " + expirationDate) - continue - } else { - b := v.client.NewRequest("POST", "/v1/auth/token/revoke-accessor") - - payload := map[string]interface{}{ - "accessor": accessor, - } - - if err := b.SetJSONBody(payload); err != nil { - return err - } - response, err := v.client.RawRequest(b) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - log.Fatal(err) - } - - if response.StatusCode == 204 { - fmt.Println("Revoked token with policy: " + matchedPolicy) - } else { - fmt.Printf("Failed with status: %s\n", response.Status) - fmt.Printf("Failed with status code: %d\n", response.StatusCode) - return errors.New("failure to revoke tokens") - } - } - } - } - - if !tokenExpiration { - b := v.client.NewRequest("POST", "/v1/auth/token/tidy") - response, err := v.client.RawRequest(b) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Tidy success status: %s\n", response.Status) - - if response.StatusCode == 202 { - var tidyResponseMap map[string]interface{} - if err = response.DecodeJSON(&tidyResponseMap); err != nil { - return err - } - if warnings, ok := tidyResponseMap["warnings"].([]interface{}); ok { - for _, warning := range warnings { - fmt.Println(warning.(string)) - } - } - } else { - fmt.Printf("Non critical tidy success failure: %s\n", response.Status) - fmt.Printf("Non critical tidy success failure: %d\n", response.StatusCode) - } - } - } - } - - return nil -} - -// CreateKVPath Creates a kv engine with the specified name and description -func (v *Vault) CreateKVPath(path string, description string) error { - return v.client.Sys().Mount(path, &api.MountInput{ - Type: "kv", - Description: description, - Options: map[string]string{"version": "2"}}) -} - -// DeleteKVPath Deletes a KV path at a specified point. -func (v *Vault) DeleteKVPath(path string) error { - return v.client.Sys().Unmount(path) -} - -// InitVault performs vault initialization and f -func (v *Vault) InitVault(keyShares int, keyThreshold int) (*KeyTokenWrapper, error) { - request := api.InitRequest{ - SecretShares: keyShares, - SecretThreshold: keyThreshold} - - response, err := v.client.Sys().Init(&request) - if err != nil { - fmt.Println("There was an error with initializing vault @ " + v.client.Address()) - return nil, err - } - // Remove for deployment - fmt.Println("Vault succesfully Init'd") - fmt.Println("=========================") - for _, key := range response.KeysB64 { - fmt.Printf("Unseal key: %s\n", key) - } - fmt.Printf("Root token: %s\n\n", response.RootToken) - - keyToken := KeyTokenWrapper{ - Keys: response.KeysB64, - Token: response.RootToken} - - return &keyToken, nil -} - -// GetExistsTokenRole - Gets the token role by token role name. -func (v *Vault) GetExistsTokenRoleFromFile(filename string) (bool, error) { - roleFile, err := os.ReadFile(filename) - if err != nil { - return false, err - } - - tokenRole := YamlNewTokenRoleOptions{} - yamlErr := yaml.Unmarshal(roleFile, &tokenRole) - if yamlErr != nil { - return false, yamlErr - } - - fmt.Printf("Role: %s\n", tokenRole.RoleName) - - r := v.client.NewRequest("GET", fmt.Sprintf("/v1/auth/token/roles/%s", tokenRole.RoleName)) - response, err := v.client.RawRequest(r) - if response != nil && response.Body != nil { - defer response.Body.Close() - } - - if err != nil { - return false, err - } - - var jsonData map[string]interface{} - if err = response.DecodeJSON(&jsonData); err != nil { - return false, err - } - - if _, ok := jsonData["data"].(map[string]interface{}); ok { - return true, nil - } - - return false, fmt.Errorf("error parsing resonse for key 'data'") -} - -// CreatePolicyFromFile Creates a policy with the given name and rules -func (v *Vault) GetExistsPolicyFromFileName(filename string) (bool, error) { - filenameParts := strings.Split(filename, ".") - - policyContent, err := v.client.Sys().GetPolicy(filenameParts[0]) - - if policyContent == "" { - return false, nil - } else if err != nil { - return true, err - } else { - return true, nil - } -} - -// CreatePolicyFromFile Creates a policy with the given name and rules -func (v *Vault) CreatePolicyFromFile(name string, filepath string) error { - data, err := os.ReadFile(filepath) - if err != nil { - return err - } - return v.client.Sys().PutPolicy(name, string(data)) -} - -// CreateEmptyPolicy Creates a policy with no permissions -func (v *Vault) CreateEmptyPolicy(name string) error { - return v.client.Sys().PutPolicy(name, "") -} - -// ValidateEnvironment Ensures token has access to requested data. -func (v *Vault) ValidateEnvironment(environment string) bool { - secret, err := v.client.Auth().Token().LookupSelf() - valid := false - if err == nil { - policies, _ := secret.TokenPolicies() - - for _, policy := range policies { - if strings.Contains(policy, environment) { - valid = true - } - } - - } - return valid -} - -// SetShards Sets known shards used by this vault for unsealing -func (v *Vault) SetShards(shards []string) { - v.shards = shards -} - -// AddShard Adds a single shard to the list of shards -func (v *Vault) AddShard(shard string) { - v.shards = append(v.shards, shard) -} - -// Unseal Performs an unseal wuth this vault's shard. Returns true if unseal is successful -func (v *Vault) Unseal() (int, int, bool, error) { - var status *api.SealStatusResponse - var err error - for _, shard := range v.shards { - status, err = v.client.Sys().Unseal(shard) - if err != nil { - return 0, 0, false, err - } - } - return status.Progress, status.T, status.Sealed, nil -} - -// CreateTokenCidrRoleFromFile Creates a new token cidr role from the given file and returns the name -func (v *Vault) CreateTokenCidrRoleFromFile(filename string) error { - tokenfile, err := os.ReadFile(filename) - if err != nil { - return err - } - tokenRole := YamlNewTokenRoleOptions{} - yamlErr := yaml.Unmarshal(tokenfile, &tokenRole) - if yamlErr != nil { - return yamlErr - } - errRoleCreate := v.CreateNewTokenCidrRole(&tokenRole) - return errRoleCreate -} - -// CreateTokenFromFile Creates a new token from the given file and returns the name -func (v *Vault) CreateTokenFromFile(filename string) (string, error) { - tokenfile, err := os.ReadFile(filename) - if err != nil { - return "", err - } - token := api.TokenCreateRequest{} - yaml.Unmarshal(tokenfile, &token) - - tokenRole := YamlNewTokenRoleOptions{} - yamlErr := yaml.Unmarshal(tokenfile, &tokenRole) - if yamlErr == nil { - if tokenRole.RoleName != "" { - response, err := v.client.Auth().Token().CreateWithRole(&token, tokenRole.RoleName) - if err != nil { - return "", err - } - return response.Auth.ClientToken, err - } - } - - response, err := v.client.Auth().Token().Create(&token) - if err != nil { - return "", err - } - return response.Auth.ClientToken, err -} - -// CreateTokenFromMap takes a map and generates a vault token, returning the token -func (v *Vault) CreateTokenFromMap(data map[string]interface{}) (string, error) { - token := &api.TokenCreateRequest{} - - // Parse input data and create request - if policies, ok := data["policies"].([]interface{}); ok { - newPolicies := []string{} - for _, p := range policies { - if newP, ok := p.(string); ok { - newPolicies = append(newPolicies, newP) - } - } - token.Policies = newPolicies - } - if meta, ok := data["meta"].(map[string]string); ok { - token.Metadata = meta - } - if TTL, ok := data["creation_ttl"].(string); ok { - token.TTL = TTL - } - if exTTL, ok := data["explicit_max_ttl"].(string); ok { - token.ExplicitMaxTTL = exTTL - } - if period, ok := data["period"].(string); ok { - token.Period = period - } - if noParent, ok := data["no_parent"].(bool); ok { - token.NoParent = noParent - } - if noDefault, ok := data["no_default_policy"].(bool); ok { - token.NoDefaultPolicy = noDefault - } - if displayName, ok := data["display_name"].(string); ok { - token.DisplayName = displayName - } - if numUses, ok := data["num_uses"].(int); ok { - token.NumUses = numUses - } - if renewable, ok := data["renewable"].(bool); ok { - token.Renewable = &renewable - } - - response, err := v.client.Auth().Token().Create(token) - if response == nil { - return "", err - } - return response.Auth.ClientToken, err -} - -// GetStatus checks the health of the vault and retrieves version and status of init/seal -func (v *Vault) GetStatus() (map[string]interface{}, error) { - health, err := v.client.Sys().Health() - if err != nil { - return nil, err - } - return map[string]interface{}{ - "initialized": health.Initialized, - "sealed": health.Sealed, - "version": health.Version, - }, nil -} - -// Proper shutdown of modifier. -func (v *Vault) Close() { - if v.httpClient != nil { - v.httpClient.CloseIdleConnections() - v.httpClient.Transport = nil - v.httpClient = nil - } -} +package system + +import ( + "errors" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + "time" + + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + + "github.com/hashicorp/vault/api" + "gopkg.in/yaml.v2" +) + +// Vault Represents a vault connection for managing the vault's properties +type Vault struct { + httpClient *http.Client // Handle to http client. + client *api.Client // Client connected to vault + shards []string // Master key shards used to unseal vault +} + +// KeyTokenWrapper Contains the unseal keys and root token +type KeyTokenWrapper struct { + Keys []string // Base 64 encoded keys + Token string // Root token for the vault +} + +// NewVault Constructs a new vault at the given address with the given access token +func NewVault(insecure bool, address string, env string, newVault bool, pingVault bool, scanVault bool, logger *log.Logger) (*Vault, error) { + return NewVaultWithNonlocal(insecure, + address, + env, + newVault, + pingVault, + scanVault, + false, // allowNonLocal - false + logger) +} + +// NewVault Constructs a new vault at the given address with the given access token allowing insecure for non local. +func NewVaultWithNonlocal(insecure bool, address string, env string, newVault bool, pingVault bool, scanVault bool, allowNonLocal bool, logger *log.Logger) (*Vault, error) { + var httpClient *http.Client + var err error + + if allowNonLocal { + httpClient, err = helperkv.CreateHTTPClientAllowNonLocal(insecure, address, env, scanVault, true) + } else { + httpClient, err = helperkv.CreateHTTPClient(insecure, address, env, scanVault) + } + + if err != nil { + logger.Println("Connection to vault couldn't be made - vaultHost: " + address) + return nil, err + } + client, err := api.NewClient(&api.Config{Address: address, HttpClient: httpClient}) + if err != nil { + logger.Println("vaultHost: " + address) + return nil, err + } + + health, err := client.Sys().Health() + if err != nil { + return nil, err + } + + if pingVault { + fmt.Println("Ping success!") + logger.Println("Ping success!") + return nil, nil + } + + if !newVault && health.Sealed { + return nil, errors.New("Vault is sealed at " + address) + } + + return &Vault{ + client: client, + shards: nil}, err +} + +// Confirms we have a valid and active connection to vault. If it doesn't, it re-establishes a new connection. +func (v *Vault) RefreshClient() error { + tries := 0 + var refreshErr error + + for tries < 3 { + if _, err := v.GetStatus(); err != nil { + if v.httpClient != nil { + v.httpClient.CloseIdleConnections() + } + + client, err := api.NewClient(&api.Config{Address: v.client.Address(), HttpClient: v.client.CloneConfig().HttpClient}) + if err != nil { + refreshErr = err + } else { + v.client = client + return nil + } + + tries = tries + 1 + } else { + tries = 3 + } + } + + return refreshErr +} + +// SetToken Stores the access token for this vault +func (v *Vault) SetToken(token string) { + v.client.SetToken(token) +} + +// GetToken Fetches current token from client +func (v *Vault) GetToken() string { + return v.client.Token() +} + +// GetTokenInfo fetches data regarding this token +func (v *Vault) GetTokenInfo(tokenName string) (map[string]interface{}, error) { + token, err := v.client.Auth().Token().Lookup(tokenName) + if token == nil { + return nil, err + } + return token.Data, err +} + +// RevokeToken If proper access given, revokes access of a token and all children +func (v *Vault) RevokeToken(token string) error { + return v.client.Auth().Token().RevokeTree(token) +} + +// RevokeSelf Revokes token of current client +func (v *Vault) RevokeSelf() error { + return v.client.Auth().Token().RevokeSelf("") +} + +// RenewSelf Renews the token associated with this vault struct +func (v *Vault) RenewSelf(increment int) error { + _, err := v.client.Auth().Token().RenewSelf(increment) + return err +} + +// GetOrRevokeTokensInScope() +func (v *Vault) GetOrRevokeTokensInScope(dir string, tokenFilter string, tokenExpiration bool, logger *log.Logger) error { + var tokenPath = dir + var tokenPolicies = []string{} + + files, err := os.ReadDir(tokenPath) + if err != nil { + log.Fatal(err) + } + + for _, f := range files { + if f.IsDir() { + continue + } + if tokenFilter != "" && !strings.HasPrefix(f.Name(), tokenFilter) { + // Token doesn't match filter... Skipping. + continue + } + var file, err = os.OpenFile(tokenPath+string(os.PathSeparator)+f.Name(), os.O_RDWR, 0644) + if file != nil { + defer file.Close() + } + if err != nil { + log.Fatal(err) + } + + byteValue, _ := io.ReadAll(file) + token := api.TokenCreateRequest{} + yaml.Unmarshal(byteValue, &token) + tokenPolicies = append(tokenPolicies, token.Policies...) + } + r := v.client.NewRequest("LIST", "/v1/auth/token/accessors") + response, err := v.client.RawRequest(r) + + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + log.Fatal(err) + } + + var jsonData map[string]interface{} + + if err = response.DecodeJSON(&jsonData); err != nil { + return err + } + + if data, ok := jsonData["data"].(map[string]interface{}); ok { + if accessors, ok := data["keys"].([]interface{}); ok { + for _, accessor := range accessors { + b := v.client.NewRequest("POST", "/v1/auth/token/lookup-accessor") + + payload := map[string]interface{}{ + "accessor": accessor, + } + + if err := b.SetJSONBody(payload); err != nil { + return err + } + response, err := v.client.RawRequest(b) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + if response.StatusCode == 403 || response.StatusCode == 400 { + // Some accessors we don't have access to, but we don't care about those. + continue + } else { + log.Fatal(err) + return err + } + } + var accessorDataMap map[string]interface{} + if err = response.DecodeJSON(&accessorDataMap); err != nil { + return err + } + var expirationDate string + var expirationDateOk bool + var matchedPolicy string + if accessorData, ok := accessorDataMap["data"].(map[string]interface{}); ok { + if expirationDate, expirationDateOk = accessorData["expire_time"].(string); expirationDateOk { + currentTime := time.Now() + expirationTime, timeError := time.Parse(time.RFC3339Nano, expirationDate) + if timeError == nil && currentTime.Before(expirationTime) { + if policies, ok := accessorData["policies"].([]interface{}); ok { + for _, policy := range policies { + for _, tokenPolicy := range tokenPolicies { + if policy.(string) == "root" || strings.EqualFold(policy.(string), tokenPolicy) { + matchedPolicy = tokenPolicy + goto revokeAccessor + } + } + } + } + } + } + continue + revokeAccessor: + if tokenExpiration { + fmt.Println("Token with the policy " + matchedPolicy + " expires on " + expirationDate) + continue + } else { + b := v.client.NewRequest("POST", "/v1/auth/token/revoke-accessor") + + payload := map[string]interface{}{ + "accessor": accessor, + } + + if err := b.SetJSONBody(payload); err != nil { + return err + } + response, err := v.client.RawRequest(b) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + log.Fatal(err) + } + + if response.StatusCode == 204 { + fmt.Println("Revoked token with policy: " + matchedPolicy) + } else { + fmt.Printf("Failed with status: %s\n", response.Status) + fmt.Printf("Failed with status code: %d\n", response.StatusCode) + return errors.New("failure to revoke tokens") + } + } + } + } + + if !tokenExpiration { + b := v.client.NewRequest("POST", "/v1/auth/token/tidy") + response, err := v.client.RawRequest(b) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Tidy success status: %s\n", response.Status) + + if response.StatusCode == 202 { + var tidyResponseMap map[string]interface{} + if err = response.DecodeJSON(&tidyResponseMap); err != nil { + return err + } + if warnings, ok := tidyResponseMap["warnings"].([]interface{}); ok { + for _, warning := range warnings { + fmt.Println(warning.(string)) + } + } + } else { + fmt.Printf("Non critical tidy success failure: %s\n", response.Status) + fmt.Printf("Non critical tidy success failure: %d\n", response.StatusCode) + } + } + } + } + + return nil +} + +// CreateKVPath Creates a kv engine with the specified name and description +func (v *Vault) CreateKVPath(path string, description string) error { + return v.client.Sys().Mount(path, &api.MountInput{ + Type: "kv", + Description: description, + Options: map[string]string{"version": "2"}}) +} + +// DeleteKVPath Deletes a KV path at a specified point. +func (v *Vault) DeleteKVPath(path string) error { + return v.client.Sys().Unmount(path) +} + +// InitVault performs vault initialization and f +func (v *Vault) InitVault(keyShares int, keyThreshold int) (*KeyTokenWrapper, error) { + request := api.InitRequest{ + SecretShares: keyShares, + SecretThreshold: keyThreshold} + + response, err := v.client.Sys().Init(&request) + if err != nil { + fmt.Println("There was an error with initializing vault @ " + v.client.Address()) + return nil, err + } + // Remove for deployment + fmt.Println("Vault succesfully Init'd") + fmt.Println("=========================") + for _, key := range response.KeysB64 { + fmt.Printf("Unseal key: %s\n", key) + } + fmt.Printf("Root token: %s\n\n", response.RootToken) + + keyToken := KeyTokenWrapper{ + Keys: response.KeysB64, + Token: response.RootToken} + + return &keyToken, nil +} + +// GetExistsTokenRole - Gets the token role by token role name. +func (v *Vault) GetExistsTokenRoleFromFile(filename string) (bool, error) { + roleFile, err := os.ReadFile(filename) + if err != nil { + return false, err + } + + tokenRole := YamlNewTokenRoleOptions{} + yamlErr := yaml.Unmarshal(roleFile, &tokenRole) + if yamlErr != nil { + return false, yamlErr + } + + fmt.Printf("Role: %s\n", tokenRole.RoleName) + + r := v.client.NewRequest("GET", fmt.Sprintf("/v1/auth/token/roles/%s", tokenRole.RoleName)) + response, err := v.client.RawRequest(r) + if response != nil && response.Body != nil { + defer response.Body.Close() + } + + if err != nil { + return false, err + } + + var jsonData map[string]interface{} + if err = response.DecodeJSON(&jsonData); err != nil { + return false, err + } + + if _, ok := jsonData["data"].(map[string]interface{}); ok { + return true, nil + } + + return false, fmt.Errorf("error parsing resonse for key 'data'") +} + +// CreatePolicyFromFile Creates a policy with the given name and rules +func (v *Vault) GetExistsPolicyFromFileName(filename string) (bool, error) { + filenameParts := strings.Split(filename, ".") + + policyContent, err := v.client.Sys().GetPolicy(filenameParts[0]) + + if policyContent == "" { + return false, nil + } else if err != nil { + return true, err + } else { + return true, nil + } +} + +// CreatePolicyFromFile Creates a policy with the given name and rules +func (v *Vault) CreatePolicyFromFile(name string, filepath string) error { + data, err := os.ReadFile(filepath) + if err != nil { + return err + } + return v.client.Sys().PutPolicy(name, string(data)) +} + +// CreateEmptyPolicy Creates a policy with no permissions +func (v *Vault) CreateEmptyPolicy(name string) error { + return v.client.Sys().PutPolicy(name, "") +} + +// ValidateEnvironment Ensures token has access to requested data. +func (v *Vault) ValidateEnvironment(environment string) bool { + secret, err := v.client.Auth().Token().LookupSelf() + valid := false + if err == nil { + policies, _ := secret.TokenPolicies() + + for _, policy := range policies { + if strings.Contains(policy, environment) { + valid = true + } + } + + } + return valid +} + +// SetShards Sets known shards used by this vault for unsealing +func (v *Vault) SetShards(shards []string) { + v.shards = shards +} + +// AddShard Adds a single shard to the list of shards +func (v *Vault) AddShard(shard string) { + v.shards = append(v.shards, shard) +} + +// Unseal Performs an unseal wuth this vault's shard. Returns true if unseal is successful +func (v *Vault) Unseal() (int, int, bool, error) { + var status *api.SealStatusResponse + var err error + for _, shard := range v.shards { + status, err = v.client.Sys().Unseal(shard) + if err != nil { + return 0, 0, false, err + } + } + return status.Progress, status.T, status.Sealed, nil +} + +// CreateTokenCidrRoleFromFile Creates a new token cidr role from the given file and returns the name +func (v *Vault) CreateTokenCidrRoleFromFile(filename string) error { + tokenfile, err := os.ReadFile(filename) + if err != nil { + return err + } + tokenRole := YamlNewTokenRoleOptions{} + yamlErr := yaml.Unmarshal(tokenfile, &tokenRole) + if yamlErr != nil { + return yamlErr + } + errRoleCreate := v.CreateNewTokenCidrRole(&tokenRole) + return errRoleCreate +} + +// CreateTokenFromFile Creates a new token from the given file and returns the name +func (v *Vault) CreateTokenFromFile(filename string) (string, error) { + tokenfile, err := os.ReadFile(filename) + if err != nil { + return "", err + } + token := api.TokenCreateRequest{} + yaml.Unmarshal(tokenfile, &token) + + tokenRole := YamlNewTokenRoleOptions{} + yamlErr := yaml.Unmarshal(tokenfile, &tokenRole) + if yamlErr == nil { + if tokenRole.RoleName != "" { + response, err := v.client.Auth().Token().CreateWithRole(&token, tokenRole.RoleName) + if err != nil { + return "", err + } + return response.Auth.ClientToken, err + } + } + + response, err := v.client.Auth().Token().Create(&token) + if err != nil { + return "", err + } + return response.Auth.ClientToken, err +} + +// CreateTokenFromMap takes a map and generates a vault token, returning the token +func (v *Vault) CreateTokenFromMap(data map[string]interface{}) (string, error) { + token := &api.TokenCreateRequest{} + + // Parse input data and create request + if policies, ok := data["policies"].([]interface{}); ok { + newPolicies := []string{} + for _, p := range policies { + if newP, ok := p.(string); ok { + newPolicies = append(newPolicies, newP) + } + } + token.Policies = newPolicies + } + if meta, ok := data["meta"].(map[string]string); ok { + token.Metadata = meta + } + if TTL, ok := data["creation_ttl"].(string); ok { + token.TTL = TTL + } + if exTTL, ok := data["explicit_max_ttl"].(string); ok { + token.ExplicitMaxTTL = exTTL + } + if period, ok := data["period"].(string); ok { + token.Period = period + } + if noParent, ok := data["no_parent"].(bool); ok { + token.NoParent = noParent + } + if noDefault, ok := data["no_default_policy"].(bool); ok { + token.NoDefaultPolicy = noDefault + } + if displayName, ok := data["display_name"].(string); ok { + token.DisplayName = displayName + } + if numUses, ok := data["num_uses"].(int); ok { + token.NumUses = numUses + } + if renewable, ok := data["renewable"].(bool); ok { + token.Renewable = &renewable + } + + response, err := v.client.Auth().Token().Create(token) + if response == nil { + return "", err + } + return response.Auth.ClientToken, err +} + +// GetStatus checks the health of the vault and retrieves version and status of init/seal +func (v *Vault) GetStatus() (map[string]interface{}, error) { + health, err := v.client.Sys().Health() + if err != nil { + return nil, err + } + return map[string]interface{}{ + "initialized": health.Initialized, + "sealed": health.Sealed, + "version": health.Version, + }, nil +} + +// Proper shutdown of modifier. +func (v *Vault) Close() { + if v.httpClient != nil { + v.httpClient.CloseIdleConnections() + v.httpClient.Transport = nil + v.httpClient = nil + } +} diff --git a/trcweb/client/client.go b/trcweb/client/client.go index 426b52a28..916b9ecb7 100644 --- a/trcweb/client/client.go +++ b/trcweb/client/client.go @@ -1,41 +1,41 @@ -package main - -import ( - "context" - "flag" - "fmt" - "net/http" - - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" -) - -func main() { - addrPtr := flag.String("addr", "http://127.0.0.1:8008", "API endpoint for the vault") - apiClient := pb.NewEnterpriseServiceBrokerProtobufClient(*addrPtr, &http.Client{}) - - makeVaultReq := &pb.GetValuesReq{} - driverConfig := &eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - }, - } - - vault, err := apiClient.GetValues(context.Background(), makeVaultReq) - eUtils.CheckError(&driverConfig.CoreConfig, err, true) - - fmt.Printf("Vault: \n") - for _, env := range vault.Envs { - fmt.Printf("Env: %s\n", env.Name) - for _, service := range env.Services { - fmt.Printf("\tService: %s\n", service.Name) - for _, file := range service.Files { - fmt.Printf("\t\tFile: %s\n", file.Name) - for _, val := range file.Values { - fmt.Printf("\t\t\tkey: %s\tvalue: %s\n", val.Key, val.Value) - } - } - } - } -} +package main + +import ( + "context" + "flag" + "fmt" + "net/http" + + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" +) + +func main() { + addrPtr := flag.String("addr", "http://127.0.0.1:8008", "API endpoint for the vault") + apiClient := pb.NewEnterpriseServiceBrokerProtobufClient(*addrPtr, &http.Client{}) + + makeVaultReq := &pb.GetValuesReq{} + driverConfig := &eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + }, + } + + vault, err := apiClient.GetValues(context.Background(), makeVaultReq) + eUtils.CheckError(&driverConfig.CoreConfig, err, true) + + fmt.Printf("Vault: \n") + for _, env := range vault.Envs { + fmt.Printf("Env: %s\n", env.Name) + for _, service := range env.Services { + fmt.Printf("\tService: %s\n", service.Name) + for _, file := range service.Files { + fmt.Printf("\t\tFile: %s\n", file.Name) + for _, val := range file.Values { + fmt.Printf("\t\t\tkey: %s\tvalue: %s\n", val.Key, val.Value) + } + } + } + } +} diff --git a/trcweb/graphQL/gql.go b/trcweb/graphQL/gql.go index 49324d163..b04ed3e68 100644 --- a/trcweb/graphQL/gql.go +++ b/trcweb/graphQL/gql.go @@ -1,316 +1,316 @@ -package main - -import ( - "context" - "encoding/json" - "errors" - "flag" - "fmt" - "net/http" - - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" - - "github.com/graphql-go/graphql" -) - -type VaultVals struct { - ID string `json:"id"` - envs []Env `json:"envs"` -} -type Env struct { - ID int `json:"id"` - name string `json:"name"` - services []Service `json:"services"` -} -type Service struct { - envID int `json: "envID"` - ID int `json:"id"` - name string `json:"name"` - files []File `json:"files"` -} -type File struct { - envID int `json: "envID"` - servID int `json: "servID"` - ID int `json:"id"` - name string `json:"name"` - values []Value `json:"values"` -} -type Value struct { - envID int `json: "envID"` - servID int `json: "servID"` - fileID int `json: "fileID"` - ID int `json:"id"` - key string `json:"name"` - value string `json:"value"` -} - -func main() { - - addrPtr := flag.String("addr", "http://127.0.0.1:8008", "API endpoint for the vault") - apiClient := pb.NewEnterpriseServiceBrokerProtobufClient(*addrPtr, &http.Client{}) - - makeVaultReq := &pb.GetValuesReq{} - driverConfig := &eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - ExitOnFailure: true, - }, - } - - vault, err := apiClient.GetValues(context.Background(), makeVaultReq) - eUtils.CheckError(&driverConfig.CoreConfig, err, true) - - driverConfig.CoreConfig.ExitOnFailure = false - - envList := []Env{} - for i, env := range vault.Envs { - serviceList := []Service{} - for j, service := range env.Services { - fileList := []File{} - for k, file := range service.Files { - valList := []Value{} - for l, val := range file.Values { - valQL := Value{ID: l, envID: i, servID: j, fileID: k, key: val.Key, value: val.Value} - valList = append(valList, valQL) - } - fileQL := File{ID: k, envID: i, servID: j, name: file.Name, values: valList} - fileList = append(fileList, fileQL) - } - serviceQL := Service{ID: j, envID: i, name: service.Name, files: fileList} - serviceList = append(serviceList, serviceQL) - } - envQL := Env{ID: i, name: env.Name, services: serviceList} - envList = append(envList, envQL) - } - - vaultQL := VaultVals{envs: envList} - var ValueObject = graphql.NewObject( - graphql.ObjectConfig{ - Name: "Value", - Fields: graphql.Fields{ - "id": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "envid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "servid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "fileid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "key": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - val := params.Source.(Value).ID - file := params.Source.(Value).fileID - serv := params.Source.(Value).servID - env := params.Source.(Value).envID - return vaultQL.envs[env].services[serv].files[file].values[val].key, nil - }, - }, - "value": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - - val := params.Source.(Value).ID - file := params.Source.(Value).fileID - serv := params.Source.(Value).servID - env := params.Source.(Value).envID - return vaultQL.envs[env].services[serv].files[file].values[val].value, nil - }, - }, - }, - }) - var FileObject = graphql.NewObject( - graphql.ObjectConfig{ - Name: "File", - Fields: graphql.Fields{ - "id": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "envid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "servid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "name": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - file := params.Source.(File).ID - serv := params.Source.(File).servID - env := params.Source.(File).envID - return vaultQL.envs[env].services[serv].files[file].name, nil - }, - }, - "values": &graphql.Field{ - Type: graphql.NewList(ValueObject), - Args: graphql.FieldConfigArgument{ - "keyName": &graphql.ArgumentConfig{ - Type: graphql.String, - }, - // "valName": &graphql.ArgumentConfig{ - // Type: graphql.String, - // }, - }, - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - //get list of values and return - keyStr, keyOK := params.Args["keyName"].(string) - // valStr, valOK := params.Args["valName"].(string) - - file := params.Source.(File).ID - serv := params.Source.(File).servID - env := params.Source.(File).envID - if keyOK { - for i, v := range vaultQL.envs[env].services[serv].files[file].values { - if v.key == keyStr { - return []Value{vaultQL.envs[env].services[serv].files[file].values[i]}, nil - } - } - return []Value{}, errors.New("keyName not found") - } //else if valOK { - // for i, v := range vaultQL.envs[env].services[serv].files[file].values { - // if v.value == valStr { - // return []Value{vaultQL.envs[env].services[serv].files[file].values[i]}, nil - // } - // } - // return vaultQL.envs[env].services[serv].files[file].values, errors.New("valName not found") - // } - return vaultQL.envs[env].services[serv].files[file].values, nil - //return nil, nil - }, - }, - }, - }) - var ServiceObject = graphql.NewObject( - graphql.ObjectConfig{ - Name: "Service", - Fields: graphql.Fields{ - "id": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "envid": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "name": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - serv := params.Source.(Service).ID - env := params.Source.(Service).envID - return vaultQL.envs[env].services[serv].name, nil - }, - }, - "files": &graphql.Field{ - Type: graphql.NewList(FileObject), - Args: graphql.FieldConfigArgument{ - "fileName": &graphql.ArgumentConfig{ - Type: graphql.String, - }, - }, - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - fileStr, isOK := params.Args["fileName"].(string) - serv := params.Source.(Service).ID - env := params.Source.(Service).envID - if isOK { - for i, f := range vaultQL.envs[env].services[serv].files { - if f.name == fileStr { - return []File{vaultQL.envs[env].services[serv].files[i]}, nil - } - } - return []File{}, errors.New("fileName not found") - } - return vaultQL.envs[env].services[serv].files, nil - }, - }, - }, - }) - var EnvObject = graphql.NewObject( - graphql.ObjectConfig{ - Name: "Env", - Fields: graphql.Fields{ - "id": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - }, - "name": &graphql.Field{ - Type: graphql.NewNonNull(graphql.String), - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - env := params.Source.(Env).ID - return vaultQL.envs[env].name, nil - }, - }, - "services": &graphql.Field{ - Type: graphql.NewList(ServiceObject), - Args: graphql.FieldConfigArgument{ - "servName": &graphql.ArgumentConfig{ - Type: graphql.String, - }, - }, - - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - - servStr, isOK := params.Args["servName"].(string) - env := params.Source.(Env).ID - if isOK { - for i, s := range vaultQL.envs[env].services { - if s.name == servStr { - return []Service{vaultQL.envs[env].services[i]}, nil - } - } - return []Service{}, errors.New("servName not found") - } - return vaultQL.envs[env].services, nil - - }, - }, - }, - }) - var VaultValObject = graphql.NewObject( - graphql.ObjectConfig{ - Name: "VaultVals", - - Fields: graphql.Fields{ - "envs": &graphql.Field{ - Type: graphql.NewList(EnvObject), - Args: graphql.FieldConfigArgument{ - "envName": &graphql.ArgumentConfig{ - Type: graphql.String, - }, - }, - Resolve: func(params graphql.ResolveParams) (interface{}, error) { - envStr, isOK := params.Args["envName"].(string) - if isOK { - for i, e := range vaultQL.envs { - if e.name == envStr { - return []Env{vaultQL.envs[i]}, nil - } - } - return []Env{}, errors.New("envName not found") - } - return vaultQL.envs, nil - - }, - }, - }, - }) - - http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") - w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") - schema, _ := graphql.NewSchema(graphql.SchemaConfig{ - Query: VaultValObject, - }) - result := graphql.Do(graphql.Params{ - Schema: schema, - RequestString: r.URL.Query().Get("query"), - }) - json.NewEncoder(w).Encode(result) - }) - - fmt.Println("Server is running on port 8090") - fmt.Println("Test with: curl -g 'http://localhost:8090/graphql?query={envs{services{files{values{key,value}}}}}'") - http.ListenAndServe(":8090", nil) -} +package main + +import ( + "context" + "encoding/json" + "errors" + "flag" + "fmt" + "net/http" + + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" + + "github.com/graphql-go/graphql" +) + +type VaultVals struct { + ID string `json:"id"` + envs []Env `json:"envs"` +} +type Env struct { + ID int `json:"id"` + name string `json:"name"` + services []Service `json:"services"` +} +type Service struct { + envID int `json: "envID"` + ID int `json:"id"` + name string `json:"name"` + files []File `json:"files"` +} +type File struct { + envID int `json: "envID"` + servID int `json: "servID"` + ID int `json:"id"` + name string `json:"name"` + values []Value `json:"values"` +} +type Value struct { + envID int `json: "envID"` + servID int `json: "servID"` + fileID int `json: "fileID"` + ID int `json:"id"` + key string `json:"name"` + value string `json:"value"` +} + +func main() { + + addrPtr := flag.String("addr", "http://127.0.0.1:8008", "API endpoint for the vault") + apiClient := pb.NewEnterpriseServiceBrokerProtobufClient(*addrPtr, &http.Client{}) + + makeVaultReq := &pb.GetValuesReq{} + driverConfig := &eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + ExitOnFailure: true, + }, + } + + vault, err := apiClient.GetValues(context.Background(), makeVaultReq) + eUtils.CheckError(&driverConfig.CoreConfig, err, true) + + driverConfig.CoreConfig.ExitOnFailure = false + + envList := []Env{} + for i, env := range vault.Envs { + serviceList := []Service{} + for j, service := range env.Services { + fileList := []File{} + for k, file := range service.Files { + valList := []Value{} + for l, val := range file.Values { + valQL := Value{ID: l, envID: i, servID: j, fileID: k, key: val.Key, value: val.Value} + valList = append(valList, valQL) + } + fileQL := File{ID: k, envID: i, servID: j, name: file.Name, values: valList} + fileList = append(fileList, fileQL) + } + serviceQL := Service{ID: j, envID: i, name: service.Name, files: fileList} + serviceList = append(serviceList, serviceQL) + } + envQL := Env{ID: i, name: env.Name, services: serviceList} + envList = append(envList, envQL) + } + + vaultQL := VaultVals{envs: envList} + var ValueObject = graphql.NewObject( + graphql.ObjectConfig{ + Name: "Value", + Fields: graphql.Fields{ + "id": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "envid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "servid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "fileid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "key": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + val := params.Source.(Value).ID + file := params.Source.(Value).fileID + serv := params.Source.(Value).servID + env := params.Source.(Value).envID + return vaultQL.envs[env].services[serv].files[file].values[val].key, nil + }, + }, + "value": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + + val := params.Source.(Value).ID + file := params.Source.(Value).fileID + serv := params.Source.(Value).servID + env := params.Source.(Value).envID + return vaultQL.envs[env].services[serv].files[file].values[val].value, nil + }, + }, + }, + }) + var FileObject = graphql.NewObject( + graphql.ObjectConfig{ + Name: "File", + Fields: graphql.Fields{ + "id": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "envid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "servid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "name": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + file := params.Source.(File).ID + serv := params.Source.(File).servID + env := params.Source.(File).envID + return vaultQL.envs[env].services[serv].files[file].name, nil + }, + }, + "values": &graphql.Field{ + Type: graphql.NewList(ValueObject), + Args: graphql.FieldConfigArgument{ + "keyName": &graphql.ArgumentConfig{ + Type: graphql.String, + }, + // "valName": &graphql.ArgumentConfig{ + // Type: graphql.String, + // }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + //get list of values and return + keyStr, keyOK := params.Args["keyName"].(string) + // valStr, valOK := params.Args["valName"].(string) + + file := params.Source.(File).ID + serv := params.Source.(File).servID + env := params.Source.(File).envID + if keyOK { + for i, v := range vaultQL.envs[env].services[serv].files[file].values { + if v.key == keyStr { + return []Value{vaultQL.envs[env].services[serv].files[file].values[i]}, nil + } + } + return []Value{}, errors.New("keyName not found") + } //else if valOK { + // for i, v := range vaultQL.envs[env].services[serv].files[file].values { + // if v.value == valStr { + // return []Value{vaultQL.envs[env].services[serv].files[file].values[i]}, nil + // } + // } + // return vaultQL.envs[env].services[serv].files[file].values, errors.New("valName not found") + // } + return vaultQL.envs[env].services[serv].files[file].values, nil + //return nil, nil + }, + }, + }, + }) + var ServiceObject = graphql.NewObject( + graphql.ObjectConfig{ + Name: "Service", + Fields: graphql.Fields{ + "id": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "envid": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "name": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + serv := params.Source.(Service).ID + env := params.Source.(Service).envID + return vaultQL.envs[env].services[serv].name, nil + }, + }, + "files": &graphql.Field{ + Type: graphql.NewList(FileObject), + Args: graphql.FieldConfigArgument{ + "fileName": &graphql.ArgumentConfig{ + Type: graphql.String, + }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + fileStr, isOK := params.Args["fileName"].(string) + serv := params.Source.(Service).ID + env := params.Source.(Service).envID + if isOK { + for i, f := range vaultQL.envs[env].services[serv].files { + if f.name == fileStr { + return []File{vaultQL.envs[env].services[serv].files[i]}, nil + } + } + return []File{}, errors.New("fileName not found") + } + return vaultQL.envs[env].services[serv].files, nil + }, + }, + }, + }) + var EnvObject = graphql.NewObject( + graphql.ObjectConfig{ + Name: "Env", + Fields: graphql.Fields{ + "id": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + }, + "name": &graphql.Field{ + Type: graphql.NewNonNull(graphql.String), + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + env := params.Source.(Env).ID + return vaultQL.envs[env].name, nil + }, + }, + "services": &graphql.Field{ + Type: graphql.NewList(ServiceObject), + Args: graphql.FieldConfigArgument{ + "servName": &graphql.ArgumentConfig{ + Type: graphql.String, + }, + }, + + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + + servStr, isOK := params.Args["servName"].(string) + env := params.Source.(Env).ID + if isOK { + for i, s := range vaultQL.envs[env].services { + if s.name == servStr { + return []Service{vaultQL.envs[env].services[i]}, nil + } + } + return []Service{}, errors.New("servName not found") + } + return vaultQL.envs[env].services, nil + + }, + }, + }, + }) + var VaultValObject = graphql.NewObject( + graphql.ObjectConfig{ + Name: "VaultVals", + + Fields: graphql.Fields{ + "envs": &graphql.Field{ + Type: graphql.NewList(EnvObject), + Args: graphql.FieldConfigArgument{ + "envName": &graphql.ArgumentConfig{ + Type: graphql.String, + }, + }, + Resolve: func(params graphql.ResolveParams) (interface{}, error) { + envStr, isOK := params.Args["envName"].(string) + if isOK { + for i, e := range vaultQL.envs { + if e.name == envStr { + return []Env{vaultQL.envs[i]}, nil + } + } + return []Env{}, errors.New("envName not found") + } + return vaultQL.envs, nil + + }, + }, + }, + }) + + http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") + schema, _ := graphql.NewSchema(graphql.SchemaConfig{ + Query: VaultValObject, + }) + result := graphql.Do(graphql.Params{ + Schema: schema, + RequestString: r.URL.Query().Get("query"), + }) + json.NewEncoder(w).Encode(result) + }) + + fmt.Println("Server is running on port 8090") + fmt.Println("Test with: curl -g 'http://localhost:8090/graphql?query={envs{services{files{values{key,value}}}}}'") + http.ListenAndServe(":8090", nil) +} diff --git a/trcweb/rpc/apinator/service.pb.go b/trcweb/rpc/apinator/service.pb.go index 86e60651e..52c791c8e 100644 --- a/trcweb/rpc/apinator/service.pb.go +++ b/trcweb/rpc/apinator/service.pb.go @@ -755,9 +755,9 @@ func (m *GetValuesReq) XXX_DiscardUnknown() { var xxx_messageInfo_GetValuesReq proto.InternalMessageInfo -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// // Initialization Messages // -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// type InitReq struct { Files []*InitReq_SeedFile `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"` Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` @@ -1009,9 +1009,9 @@ func (m *EnvResp) GetEnv() []string { return nil } -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// // Web App LogIn Messages // -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// type VaultStatus struct { Initialized bool `protobuf:"varint,1,opt,name=initialized,proto3" json:"initialized,omitempty"` Sealed bool `protobuf:"varint,2,opt,name=sealed,proto3" json:"sealed,omitempty"` @@ -1271,9 +1271,9 @@ func (m *UnsealResp) GetNeeded() int32 { return 0 } -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// // GraphQL Messages // -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// type GraphQLQuery struct { Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1634,9 +1634,9 @@ func (m *GQLTemplateResp) GetErrors() []*GQLError { return nil } -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// // Update API Messages // -///////////////////////////////////////////////////////// +// /////////////////////////////////////////////////////// type UpdateAPIReq struct { Build string `protobuf:"bytes,1,opt,name=build,proto3" json:"build,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -1676,9 +1676,9 @@ func (m *UpdateAPIReq) GetBuild() string { return "" } -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// // Token Messages // -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// type TokensReq struct { AppRoleID string `protobuf:"bytes,1,opt,name=appRoleID,proto3" json:"appRoleID,omitempty"` AppRoleSecretID string `protobuf:"bytes,2,opt,name=appRoleSecretID,proto3" json:"appRoleSecretID,omitempty"` @@ -1820,9 +1820,9 @@ func (m *TokensResp_Token) GetValue() string { return "" } -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// // Reset Server Messages // -//////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////// type CheckConnResp struct { Connected bool `protobuf:"varint,1,opt,name=connected,proto3" json:"connected,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` diff --git a/trcweb/rpc/apinator/service.twirp.go b/trcweb/rpc/apinator/service.twirp.go index e307f3615..42523159e 100644 --- a/trcweb/rpc/apinator/service.twirp.go +++ b/trcweb/rpc/apinator/service.twirp.go @@ -6,6 +6,7 @@ Package apinator is a generated twirp stub package. This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v5.7.0. It is generated from these files: + rpc/apinator/service.proto */ package apinator @@ -519,7 +520,7 @@ type enterpriseServiceBrokerServer struct { func NewEnterpriseServiceBrokerServer(svc EnterpriseServiceBroker, hooks *twirp.ServerHooks) TwirpServer { return &enterpriseServiceBrokerServer{ EnterpriseServiceBroker: svc, - hooks: hooks, + hooks: hooks, } } @@ -2908,7 +2909,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, url string, in, o return wrapInternal(err, "could not build request") } response, err := client.Do(req) - + if err != nil { return wrapInternal(err, "failed to do request") } diff --git a/trcweb/server/InitVault.go b/trcweb/server/InitVault.go index 06bb61142..ad857337a 100644 --- a/trcweb/server/InitVault.go +++ b/trcweb/server/InitVault.go @@ -1,3 +1,3 @@ -package server - -// TODO -- wipe me. +package server + +// TODO -- wipe me. diff --git a/trcweb/server/VaultInit.go b/trcweb/server/VaultInit.go index 666253a09..f2f1ebb28 100644 --- a/trcweb/server/VaultInit.go +++ b/trcweb/server/VaultInit.go @@ -1,263 +1,263 @@ -package server - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "log" - - "github.com/trimble-oss/tierceron/pkg/core" - il "github.com/trimble-oss/tierceron/pkg/trcinit/initlib" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - sys "github.com/trimble-oss/tierceron/pkg/vaulthelper/system" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" -) - -const tokenPath string = "token_files" -const policyPath string = "policy_files" -const templatePath string = "template_files" - -// InitVault Takes init request and inits/seeds vault with contained file data -func (s *Server) InitVault(ctx context.Context, req *pb.InitReq) (*pb.InitResp, error) { - logBuffer := new(bytes.Buffer) - logger := log.New(logBuffer, "[INIT]", log.LstdFlags) - - fmt.Println("Initing vault") - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - - v, err := sys.NewVault(false, s.VaultAddr, "nonprod", true, false, false, logger) - if err != nil { - eUtils.LogErrorObject(config, err, false) - eUtils.LogErrorObject(config, err, false) - return &pb.InitResp{ - Success: false, - Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), - Tokens: nil, - }, err - } - - // Init and unseal vault - keyToken, err := v.InitVault(3, 5) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return &pb.InitResp{ - Success: false, - Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), - Tokens: nil, - }, err - } - v.SetToken(keyToken.Token) - v.SetShards(keyToken.Keys) - s.VaultToken = keyToken.Token - //check error returned by unseal - v.Unseal() - if err != nil { - eUtils.LogErrorObject(config, err, false) - return &pb.InitResp{ - Success: false, - Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), - Tokens: nil, - }, err - } - - logger.Printf("Succesfully connected to vault at %s\n", s.VaultAddr) - - // Create engines - il.CreateEngines(config, v) - - for _, seed := range req.Files { - fBytes, err := base64.StdEncoding.DecodeString(seed.Data) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return &pb.InitResp{ - Success: false, - Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), - Tokens: nil, - }, err - } - il.SeedVaultFromData(&eUtils.DriverConfig{ - CoreConfig: core.CoreConfig{ - WantCerts: true, - ExitOnFailure: true, - Log: logger, - }, - Insecure: false, VaultAddress: s.VaultAddr, Token: s.VaultToken, Env: seed.Env, ServicesWanted: []string{""}}, "", fBytes) - } - - il.UploadPolicies(config, policyPath, v, false) - - tokens := il.UploadTokens(config, tokenPath, nil, v) - tokenMap := map[string]interface{}{} - for _, token := range tokens { - tokenMap[token.Name] = token.Value - } - - mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) - eUtils.LogErrorObject(config, err, false) - - mod.EnvBasis = "bamboo" - mod.Env = "bamboo" - warn, err := mod.Write("super-secrets/tokens", tokenMap, config.Log) - eUtils.LogErrorObject(config, err, false) - eUtils.LogWarningsObject(config, warn, false) - - envStrings := SelectedEnvironment - for _, e := range envStrings { - mod.Env = e - warn, err = il.UploadTemplateDirectory(config, mod, templatePath, nil) - eUtils.LogErrorObject(config, err, false) - eUtils.LogWarningsObject(config, warn, false) - } - - err = v.EnableAppRole() - eUtils.LogErrorObject(config, err, false) - - err = v.CreateNewRole("bamboo", &sys.NewRoleOptions{ - TokenTTL: "10m", - TokenMaxTTL: "15m", - Policies: []string{"bamboo"}, - }) - eUtils.LogErrorObject(config, err, false) - - roleID, _, err := v.GetRoleID("bamboo") - eUtils.LogErrorObject(config, err, false) - - secretID, err := v.GetSecretID("bamboo") - eUtils.LogErrorObject(config, err, false) - - s.Log.Println("Init Log \n" + logBuffer.String()) - - logger.SetPrefix("[AUTH]") - logger.Printf("Role ID: %s\n", roleID) - logger.Printf("Secret ID: %s\n", secretID) - - s.InitGQL() - var targetEnv string - for _, e := range envStrings { - targetEnv = e - if e == "dev" { - break - } else if e == "staging" { - SelectedEnvironment = SelectedWebEnvironment - break - } - } - s.InitConfig(config, targetEnv) - - res, err := s.APILogin(ctx, &pb.LoginReq{Username: req.Username, Password: req.Password, Environment: targetEnv}) - if err != nil { - eUtils.LogErrorObject(config, err, false) - tokens = append(tokens, &pb.InitResp_Token{ - Name: "Auth", - Value: "invalid", - }) - } else if !res.Success { - tokens = append(tokens, &pb.InitResp_Token{ - Name: "Auth", - Value: "invalid", - }) - } else { - tokens = append(tokens, &pb.InitResp_Token{ - Name: "Auth", - Value: res.AuthToken, - }) - } - - if sToken, ok := tokenMap["webapp"].(string); ok { - s.VaultToken = sToken - } else { - s.VaultToken = "" - } - - return &pb.InitResp{ - Success: true, - Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), - Tokens: tokens, - }, nil -} - -// APILogin Verifies the user's login with the cubbyhole -func (s *Server) APILogin(ctx context.Context, req *pb.LoginReq) (*pb.LoginResp, error) { - result := pb.LoginResp{ - Success: false, - AuthToken: "", - } - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - - mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return &result, err - } - mod.Env = req.Environment - - authSuccess, name, err := s.authUser(config, mod, req.Username, req.Password) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return &result, err - } - - token, errJwtGen := s.generateJWT(name, req.Environment+"/"+req.Username, mod) - if errJwtGen != nil { - eUtils.LogErrorObject(config, errJwtGen, false) - return &result, err - } - result.AuthToken = token - result.Success = authSuccess - - return &result, nil -} - -// GetStatus requests version info and whether the vault has been initailized -func (s *Server) GetStatus(ctx context.Context, req *pb.NoParams) (*pb.VaultStatus, error) { - v, err := sys.NewVault(false, s.VaultAddr, "nonprod", true, false, false, s.Log) - if v != nil { - defer v.Close() - } - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - status, err := v.GetStatus() - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - return &pb.VaultStatus{ - Version: status["version"].(string), - Initialized: status["initialized"].(bool), - Sealed: status["sealed"].(bool), - }, nil -} - -// Unseal passes the unseal key to the vault and tries to unseal the vault -func (s *Server) Unseal(ctx context.Context, req *pb.UnsealReq) (*pb.UnsealResp, error) { - v, err := sys.NewVault(false, s.VaultAddr, "nonprod", false, false, false, s.Log) - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - v.AddShard(req.UnsealKey) - prog, need, sealed, err := v.Unseal() - - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - if sealed { - s.Log.Printf("%d/%d unseal shards\n", prog, need) - } else { - s.Log.Println("Vault successfully unsealed") - } - return &pb.UnsealResp{ - Sealed: sealed, - Progress: int32(prog), - Needed: int32(need), - }, nil -} +package server + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "log" + + "github.com/trimble-oss/tierceron/pkg/core" + il "github.com/trimble-oss/tierceron/pkg/trcinit/initlib" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + sys "github.com/trimble-oss/tierceron/pkg/vaulthelper/system" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" +) + +const tokenPath string = "token_files" +const policyPath string = "policy_files" +const templatePath string = "template_files" + +// InitVault Takes init request and inits/seeds vault with contained file data +func (s *Server) InitVault(ctx context.Context, req *pb.InitReq) (*pb.InitResp, error) { + logBuffer := new(bytes.Buffer) + logger := log.New(logBuffer, "[INIT]", log.LstdFlags) + + fmt.Println("Initing vault") + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + + v, err := sys.NewVault(false, s.VaultAddr, "nonprod", true, false, false, logger) + if err != nil { + eUtils.LogErrorObject(config, err, false) + eUtils.LogErrorObject(config, err, false) + return &pb.InitResp{ + Success: false, + Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), + Tokens: nil, + }, err + } + + // Init and unseal vault + keyToken, err := v.InitVault(3, 5) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return &pb.InitResp{ + Success: false, + Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), + Tokens: nil, + }, err + } + v.SetToken(keyToken.Token) + v.SetShards(keyToken.Keys) + s.VaultToken = keyToken.Token + //check error returned by unseal + v.Unseal() + if err != nil { + eUtils.LogErrorObject(config, err, false) + return &pb.InitResp{ + Success: false, + Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), + Tokens: nil, + }, err + } + + logger.Printf("Succesfully connected to vault at %s\n", s.VaultAddr) + + // Create engines + il.CreateEngines(config, v) + + for _, seed := range req.Files { + fBytes, err := base64.StdEncoding.DecodeString(seed.Data) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return &pb.InitResp{ + Success: false, + Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), + Tokens: nil, + }, err + } + il.SeedVaultFromData(&eUtils.DriverConfig{ + CoreConfig: core.CoreConfig{ + WantCerts: true, + ExitOnFailure: true, + Log: logger, + }, + Insecure: false, VaultAddress: s.VaultAddr, Token: s.VaultToken, Env: seed.Env, ServicesWanted: []string{""}}, "", fBytes) + } + + il.UploadPolicies(config, policyPath, v, false) + + tokens := il.UploadTokens(config, tokenPath, nil, v) + tokenMap := map[string]interface{}{} + for _, token := range tokens { + tokenMap[token.Name] = token.Value + } + + mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) + eUtils.LogErrorObject(config, err, false) + + mod.EnvBasis = "bamboo" + mod.Env = "bamboo" + warn, err := mod.Write("super-secrets/tokens", tokenMap, config.Log) + eUtils.LogErrorObject(config, err, false) + eUtils.LogWarningsObject(config, warn, false) + + envStrings := SelectedEnvironment + for _, e := range envStrings { + mod.Env = e + warn, err = il.UploadTemplateDirectory(config, mod, templatePath, nil) + eUtils.LogErrorObject(config, err, false) + eUtils.LogWarningsObject(config, warn, false) + } + + err = v.EnableAppRole() + eUtils.LogErrorObject(config, err, false) + + err = v.CreateNewRole("bamboo", &sys.NewRoleOptions{ + TokenTTL: "10m", + TokenMaxTTL: "15m", + Policies: []string{"bamboo"}, + }) + eUtils.LogErrorObject(config, err, false) + + roleID, _, err := v.GetRoleID("bamboo") + eUtils.LogErrorObject(config, err, false) + + secretID, err := v.GetSecretID("bamboo") + eUtils.LogErrorObject(config, err, false) + + s.Log.Println("Init Log \n" + logBuffer.String()) + + logger.SetPrefix("[AUTH]") + logger.Printf("Role ID: %s\n", roleID) + logger.Printf("Secret ID: %s\n", secretID) + + s.InitGQL() + var targetEnv string + for _, e := range envStrings { + targetEnv = e + if e == "dev" { + break + } else if e == "staging" { + SelectedEnvironment = SelectedWebEnvironment + break + } + } + s.InitConfig(config, targetEnv) + + res, err := s.APILogin(ctx, &pb.LoginReq{Username: req.Username, Password: req.Password, Environment: targetEnv}) + if err != nil { + eUtils.LogErrorObject(config, err, false) + tokens = append(tokens, &pb.InitResp_Token{ + Name: "Auth", + Value: "invalid", + }) + } else if !res.Success { + tokens = append(tokens, &pb.InitResp_Token{ + Name: "Auth", + Value: "invalid", + }) + } else { + tokens = append(tokens, &pb.InitResp_Token{ + Name: "Auth", + Value: res.AuthToken, + }) + } + + if sToken, ok := tokenMap["webapp"].(string); ok { + s.VaultToken = sToken + } else { + s.VaultToken = "" + } + + return &pb.InitResp{ + Success: true, + Logfile: base64.StdEncoding.EncodeToString(logBuffer.Bytes()), + Tokens: tokens, + }, nil +} + +// APILogin Verifies the user's login with the cubbyhole +func (s *Server) APILogin(ctx context.Context, req *pb.LoginReq) (*pb.LoginResp, error) { + result := pb.LoginResp{ + Success: false, + AuthToken: "", + } + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + + mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return &result, err + } + mod.Env = req.Environment + + authSuccess, name, err := s.authUser(config, mod, req.Username, req.Password) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return &result, err + } + + token, errJwtGen := s.generateJWT(name, req.Environment+"/"+req.Username, mod) + if errJwtGen != nil { + eUtils.LogErrorObject(config, errJwtGen, false) + return &result, err + } + result.AuthToken = token + result.Success = authSuccess + + return &result, nil +} + +// GetStatus requests version info and whether the vault has been initailized +func (s *Server) GetStatus(ctx context.Context, req *pb.NoParams) (*pb.VaultStatus, error) { + v, err := sys.NewVault(false, s.VaultAddr, "nonprod", true, false, false, s.Log) + if v != nil { + defer v.Close() + } + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + status, err := v.GetStatus() + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + return &pb.VaultStatus{ + Version: status["version"].(string), + Initialized: status["initialized"].(bool), + Sealed: status["sealed"].(bool), + }, nil +} + +// Unseal passes the unseal key to the vault and tries to unseal the vault +func (s *Server) Unseal(ctx context.Context, req *pb.UnsealReq) (*pb.UnsealResp, error) { + v, err := sys.NewVault(false, s.VaultAddr, "nonprod", false, false, false, s.Log) + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + v.AddShard(req.UnsealKey) + prog, need, sealed, err := v.Unseal() + + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + if sealed { + s.Log.Printf("%d/%d unseal shards\n", prog, need) + } else { + s.Log.Println("Vault successfully unsealed") + } + return &pb.UnsealResp{ + Sealed: sealed, + Progress: int32(prog), + Needed: int32(need), + }, nil +} diff --git a/trcweb/server/gqlTemplates.go b/trcweb/server/gqlTemplates.go index 256d8a42f..37979ea45 100644 --- a/trcweb/server/gqlTemplates.go +++ b/trcweb/server/gqlTemplates.go @@ -1,198 +1,198 @@ -package server - -import ( - "fmt" - "sort" - "strconv" - "strings" - "time" - - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" -) - -// getTemplateData Fetches all keys listed under 'templates' substituting private values with verification -// Secret values will only be populated for environments with values for that secret group -// All template keys that reference public values will be populated with those values -func (s *Server) getTemplateData() (*pb.ValuesRes, error) { - mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) - config := &core.CoreConfig{ - ExitOnFailure: false, - Log: s.Log, - } - - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - envStrings := SelectedEnvironment - //Only display staging in prod mode - for i, other := range envStrings { - if other == "prod" { - envStrings = append(envStrings[:i], envStrings[i+1:]...) - break - } - } - for _, e := range envStrings { - mod.Env = "local/" + e - userPaths, err := mod.List("values/", s.Log) - if err != nil { - return nil, err - } - if userPaths == nil { - continue - } - if localEnvs, ok := userPaths.Data["keys"]; ok { - for _, env := range localEnvs.([]interface{}) { - envStrings = append(envStrings, strings.Trim("local/"+e+"/"+env.(string), "/")) - } - } - } - - environments := []*pb.ValuesRes_Env{} - for _, env := range envStrings { - mod.Env = env - projects := []*pb.ValuesRes_Env_Project{} - projectPaths, err := s.getPaths(config, mod, "templates/") - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - for _, projectPath := range projectPaths { - services := []*pb.ValuesRes_Env_Project_Service{} - servicePaths, err := s.getPaths(config, mod, projectPath) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - for _, servicePath := range servicePaths { - files := []*pb.ValuesRes_Env_Project_Service_File{} - filePaths, err := s.getTemplateFilePaths(config, mod, servicePath) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - if len(filePaths) > 0 { - for _, filePath := range filePaths { - // Skip directories containing just the template file - if filePath[len(filePath)-1] == '/' { - continue - } - kvs, err := mod.ReadData(filePath) - secrets := []*pb.ValuesRes_Env_Project_Service_File_Value{} - if err != nil { - return nil, err - } - //Get metadata of versions for each filePath - versions, err := mod.ReadVersionMetadata(filePath, s.Log) - if err != nil { - return nil, err - } - var dates []time.Time - for _, v := range versions { - if val, ok := v.(map[string]interface{}); ok { - location, _ := time.LoadLocation("America/Los_Angeles") - creationTime := fmt.Sprintf("%s", val["created_time"]) - t, _ := time.Parse(time.RFC3339, creationTime) - t = t.In(location) - dates = append(dates, t) - } - } - sort.Slice(dates, func(i, j int) bool { - return dates[i].Before(dates[j]) - }) - for i := range dates { - year, month, day := dates[i].Date() - hour, min, sec := dates[i].Clock() - creationDate := strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(day) - creationHour := strconv.Itoa(hour) + ":" + strconv.Itoa(min) + ":" + strconv.Itoa(sec) - s := []string{creationDate, creationHour} - creationTime := strings.Join(s, " ") - secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: fmt.Sprint(i), Value: creationTime, Source: "versions"}) - } - // Find secrets groups in this environment - vSecret, err := mod.List("super-secrets", s.Log) - if err != nil { - return nil, err - } - availableSecrets := map[string]bool{} - if vSecret == nil { - s.Log.Println("Unable to retrieve accessible secret groups for", env) - continue - - } else { - // Construct a string -> bool map to track accessible environments - - if vDataKeys, ok := vSecret.Data["keys"]; ok { - if vKeys, okKeys := vDataKeys.([]interface{}); okKeys { - for _, k := range vKeys { - if group, ok := k.(string); ok { - availableSecrets[group] = true - } - } - } else { - return nil, fmt.Errorf("unable to retrieve accessible secret groups for %s", env) - } - } else { - return nil, fmt.Errorf("unable to retrieve accessible secret groups for %s", env) - } - } - - for k, v := range kvs { - // Get path to secret - if val, ok := v.([]interface{}); ok { - if fullPath, ok := val[0].(string); ok { - pathBlocks := strings.SplitN(fullPath, "/", 2) // Check that environment contains secret and check verification - if pathBlocks[0] == "super-secrets" && availableSecrets[pathBlocks[1]] { - validity, err := mod.ReadData("verification/" + pathBlocks[1]) - if err != nil { - return nil, err - } - if valid, ok := validity["verified"].(bool); ok { - if valid { - secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "verifiedGood", Source: "templates"}) - } else { - secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "verifiedBad", Source: "templates"}) - } - } else { - secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "unverified", Source: "templates"}) - } - } else if pathBlocks[0] == "values" { // Real value, fetch and populate - if key, ok := val[1].(string); ok { - value, err := mod.ReadValue(fullPath, key) - if err == nil && value != "" { - secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: value, Source: "value"}) - } - } else { - continue - } - } - } - } - } - //if you want to add extra dirs to filename, do it here - if len(secrets) > 0 { - files = append(files, &pb.ValuesRes_Env_Project_Service_File{Name: getPathEnd(filePath), Values: secrets}) - } - } - } - if len(files) > 0 { - services = append(services, &pb.ValuesRes_Env_Project_Service{Name: getPathEnd(servicePath), Files: files}) - } - } - if len(services) > 0 { - projects = append(projects, &pb.ValuesRes_Env_Project{Name: getPathEnd(projectPath), Services: services}) - } - } - - if len(projects) > 0 { - envName := strings.Trim(mod.Env, "/") - environments = append(environments, &pb.ValuesRes_Env{Name: string(envName), Projects: projects}) - } - - } - return &pb.ValuesRes{Envs: environments}, nil -} +package server + +import ( + "fmt" + "sort" + "strconv" + "strings" + "time" + + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" +) + +// getTemplateData Fetches all keys listed under 'templates' substituting private values with verification +// Secret values will only be populated for environments with values for that secret group +// All template keys that reference public values will be populated with those values +func (s *Server) getTemplateData() (*pb.ValuesRes, error) { + mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) + config := &core.CoreConfig{ + ExitOnFailure: false, + Log: s.Log, + } + + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + envStrings := SelectedEnvironment + //Only display staging in prod mode + for i, other := range envStrings { + if other == "prod" { + envStrings = append(envStrings[:i], envStrings[i+1:]...) + break + } + } + for _, e := range envStrings { + mod.Env = "local/" + e + userPaths, err := mod.List("values/", s.Log) + if err != nil { + return nil, err + } + if userPaths == nil { + continue + } + if localEnvs, ok := userPaths.Data["keys"]; ok { + for _, env := range localEnvs.([]interface{}) { + envStrings = append(envStrings, strings.Trim("local/"+e+"/"+env.(string), "/")) + } + } + } + + environments := []*pb.ValuesRes_Env{} + for _, env := range envStrings { + mod.Env = env + projects := []*pb.ValuesRes_Env_Project{} + projectPaths, err := s.getPaths(config, mod, "templates/") + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + for _, projectPath := range projectPaths { + services := []*pb.ValuesRes_Env_Project_Service{} + servicePaths, err := s.getPaths(config, mod, projectPath) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + for _, servicePath := range servicePaths { + files := []*pb.ValuesRes_Env_Project_Service_File{} + filePaths, err := s.getTemplateFilePaths(config, mod, servicePath) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + if len(filePaths) > 0 { + for _, filePath := range filePaths { + // Skip directories containing just the template file + if filePath[len(filePath)-1] == '/' { + continue + } + kvs, err := mod.ReadData(filePath) + secrets := []*pb.ValuesRes_Env_Project_Service_File_Value{} + if err != nil { + return nil, err + } + //Get metadata of versions for each filePath + versions, err := mod.ReadVersionMetadata(filePath, s.Log) + if err != nil { + return nil, err + } + var dates []time.Time + for _, v := range versions { + if val, ok := v.(map[string]interface{}); ok { + location, _ := time.LoadLocation("America/Los_Angeles") + creationTime := fmt.Sprintf("%s", val["created_time"]) + t, _ := time.Parse(time.RFC3339, creationTime) + t = t.In(location) + dates = append(dates, t) + } + } + sort.Slice(dates, func(i, j int) bool { + return dates[i].Before(dates[j]) + }) + for i := range dates { + year, month, day := dates[i].Date() + hour, min, sec := dates[i].Clock() + creationDate := strconv.Itoa(year) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(day) + creationHour := strconv.Itoa(hour) + ":" + strconv.Itoa(min) + ":" + strconv.Itoa(sec) + s := []string{creationDate, creationHour} + creationTime := strings.Join(s, " ") + secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: fmt.Sprint(i), Value: creationTime, Source: "versions"}) + } + // Find secrets groups in this environment + vSecret, err := mod.List("super-secrets", s.Log) + if err != nil { + return nil, err + } + availableSecrets := map[string]bool{} + if vSecret == nil { + s.Log.Println("Unable to retrieve accessible secret groups for", env) + continue + + } else { + // Construct a string -> bool map to track accessible environments + + if vDataKeys, ok := vSecret.Data["keys"]; ok { + if vKeys, okKeys := vDataKeys.([]interface{}); okKeys { + for _, k := range vKeys { + if group, ok := k.(string); ok { + availableSecrets[group] = true + } + } + } else { + return nil, fmt.Errorf("unable to retrieve accessible secret groups for %s", env) + } + } else { + return nil, fmt.Errorf("unable to retrieve accessible secret groups for %s", env) + } + } + + for k, v := range kvs { + // Get path to secret + if val, ok := v.([]interface{}); ok { + if fullPath, ok := val[0].(string); ok { + pathBlocks := strings.SplitN(fullPath, "/", 2) // Check that environment contains secret and check verification + if pathBlocks[0] == "super-secrets" && availableSecrets[pathBlocks[1]] { + validity, err := mod.ReadData("verification/" + pathBlocks[1]) + if err != nil { + return nil, err + } + if valid, ok := validity["verified"].(bool); ok { + if valid { + secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "verifiedGood", Source: "templates"}) + } else { + secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "verifiedBad", Source: "templates"}) + } + } else { + secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: "unverified", Source: "templates"}) + } + } else if pathBlocks[0] == "values" { // Real value, fetch and populate + if key, ok := val[1].(string); ok { + value, err := mod.ReadValue(fullPath, key) + if err == nil && value != "" { + secrets = append(secrets, &pb.ValuesRes_Env_Project_Service_File_Value{Key: k, Value: value, Source: "value"}) + } + } else { + continue + } + } + } + } + } + //if you want to add extra dirs to filename, do it here + if len(secrets) > 0 { + files = append(files, &pb.ValuesRes_Env_Project_Service_File{Name: getPathEnd(filePath), Values: secrets}) + } + } + } + if len(files) > 0 { + services = append(services, &pb.ValuesRes_Env_Project_Service{Name: getPathEnd(servicePath), Files: files}) + } + } + if len(services) > 0 { + projects = append(projects, &pb.ValuesRes_Env_Project{Name: getPathEnd(projectPath), Services: services}) + } + } + + if len(projects) > 0 { + envName := strings.Trim(mod.Env, "/") + environments = append(environments, &pb.ValuesRes_Env{Name: string(envName), Projects: projects}) + } + + } + return &pb.ValuesRes{Envs: environments}, nil +} diff --git a/trcweb/server/session.go b/trcweb/server/session.go index cc7378219..141f482b9 100644 --- a/trcweb/server/session.go +++ b/trcweb/server/session.go @@ -1,167 +1,167 @@ -package server - -import ( - "database/sql" - "encoding/json" - "errors" - "fmt" - "regexp" - "strings" - "time" - - "github.com/trimble-oss/tierceron/buildopts" - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - - "github.com/trimble-oss/tierceron/buildopts/coreopts" - - //mysql and mssql go libraries - _ "github.com/denisenkom/go-mssqldb" -) - -func (s *Server) authUser(config *core.CoreConfig, mod *helperkv.Modifier, operatorId string, operatorPassword string) (bool, string, error) { - connInfo, err := mod.ReadData("apiLogins/meta") - if err != nil { - return false, "", err - } - - var url, username, password string - url, ok := connInfo["sessionDB"].(string) - if !ok { - return false, "", fmt.Errorf("database connection not a string or not found") - } - username, ok = connInfo["user"].(string) - if !ok { - return false, "", fmt.Errorf("username connection not a string or not found") - } - password, ok = connInfo["pass"].(string) - if !ok { - return false, "", fmt.Errorf("password connection not a string or not found") - } - - driver, server, port, dbname, parseError := parseURL(config, url) - if parseError != nil { - return false, "", parseError - } - - if len(port) == 0 { - port = "1433" - } - db, err := sql.Open(driver, ("server=" + server + ";user id=" + username + ";password=" + password + ";port=" + port + ";database=" + dbname + ";encrypt=true;TrustServerCertificate=true")) - if db != nil { - defer db.Close() - } - if err != nil { - return false, "", err - } - - return buildopts.BuildOptions.Authorize(db, operatorId, operatorPassword) -} - -func (s *Server) getActiveSessions(config *core.CoreConfig, env string) ([]map[string]interface{}, error) { - mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) - if err != nil { - return nil, err - } - mod.Env = env - connInfo, err := mod.ReadData("apiLogins/meta") - - var url, username, password string - url, ok := connInfo["sessionDB"].(string) - if !ok { - return nil, fmt.Errorf("database connection not a string or not found") - } - username, ok = connInfo["user"].(string) - if !ok { - return nil, fmt.Errorf("username connection not a string or not found") - } - password, ok = connInfo["pass"].(string) - if !ok { - return nil, fmt.Errorf("password connection not a string or not found") - } - - driver, server, port, dbname, parseError := parseURL(config, url) - if err != nil { - return nil, parseError - } - if len(port) == 0 { - port = "1433" - } - db, err := sql.Open(driver, ("server=" + server + ";user id=" + username + ";password=" + password + ";port=" + port + ";database=" + dbname + ";encrypt=true;TrustServerCertificate=true")) - if db != nil { - defer db.Close() - } - if err != nil { - return nil, err - } - - return coreopts.BuildOptions.ActiveSessions(db) -} - -func parseURL(config *core.CoreConfig, url string) (string, string, string, string, error) { - //only works with jdbc:mysql or jdbc:sqlserver. - regex := regexp.MustCompile(`(?i)(mysql|sqlserver)://([\w\-\.]+)(?::(\d{0,5}))?(?:/|.*;DatabaseName=)(\w+).*`) - m := regex.FindStringSubmatch(url) - if m == nil { - parseFailureErr := errors.New("incorrect URL format") - eUtils.LogErrorMessage(config, parseFailureErr.Error(), true) - return "", "", "", "", parseFailureErr - } - return m[1], m[2], m[3], m[4], nil -} - -func (s *Server) getVaultSessions(env string) ([]map[string]interface{}, error) { - mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) - if err != nil { - return nil, err - } - mod.Env = "" - - sessions := []map[string]interface{}{} - paths, err := mod.List("apiLogins/"+env, s.Log) - if paths == nil { - return nil, fmt.Errorf("Nothing found under apiLogins/" + env) - } - if err != nil { - return nil, err - } - mod.Env = env - - // Pass through all registered users - var id int - if users, ok := paths.Data["keys"].([]interface{}); ok { - for _, user := range users { - if user == "meta" { - continue - } - userData, err := mod.ReadData("apiLogins/" + user.(string)) - if err != nil { - return nil, err - } - - issued, err := userData["Issued"].(json.Number).Int64() - if err != nil { - return nil, err - } - expires, err := userData["Expires"].(json.Number).Int64() - if err != nil { - return nil, err - } - // Check if session has expired - if expires < time.Now().Unix() { - userData["Issued"] = -1 - userData["Expires"] = -1 - } else { - sessions = append(sessions, map[string]interface{}{ - "ID": id, - "User": strings.TrimSpace(user.(string)), - "LastLogIn": issued, - }) - id++ - } - } - } - - return sessions, nil -} +package server + +import ( + "database/sql" + "encoding/json" + "errors" + "fmt" + "regexp" + "strings" + "time" + + "github.com/trimble-oss/tierceron/buildopts" + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + + "github.com/trimble-oss/tierceron/buildopts/coreopts" + + //mysql and mssql go libraries + _ "github.com/denisenkom/go-mssqldb" +) + +func (s *Server) authUser(config *core.CoreConfig, mod *helperkv.Modifier, operatorId string, operatorPassword string) (bool, string, error) { + connInfo, err := mod.ReadData("apiLogins/meta") + if err != nil { + return false, "", err + } + + var url, username, password string + url, ok := connInfo["sessionDB"].(string) + if !ok { + return false, "", fmt.Errorf("database connection not a string or not found") + } + username, ok = connInfo["user"].(string) + if !ok { + return false, "", fmt.Errorf("username connection not a string or not found") + } + password, ok = connInfo["pass"].(string) + if !ok { + return false, "", fmt.Errorf("password connection not a string or not found") + } + + driver, server, port, dbname, parseError := parseURL(config, url) + if parseError != nil { + return false, "", parseError + } + + if len(port) == 0 { + port = "1433" + } + db, err := sql.Open(driver, ("server=" + server + ";user id=" + username + ";password=" + password + ";port=" + port + ";database=" + dbname + ";encrypt=true;TrustServerCertificate=true")) + if db != nil { + defer db.Close() + } + if err != nil { + return false, "", err + } + + return buildopts.BuildOptions.Authorize(db, operatorId, operatorPassword) +} + +func (s *Server) getActiveSessions(config *core.CoreConfig, env string) ([]map[string]interface{}, error) { + mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) + if err != nil { + return nil, err + } + mod.Env = env + connInfo, err := mod.ReadData("apiLogins/meta") + + var url, username, password string + url, ok := connInfo["sessionDB"].(string) + if !ok { + return nil, fmt.Errorf("database connection not a string or not found") + } + username, ok = connInfo["user"].(string) + if !ok { + return nil, fmt.Errorf("username connection not a string or not found") + } + password, ok = connInfo["pass"].(string) + if !ok { + return nil, fmt.Errorf("password connection not a string or not found") + } + + driver, server, port, dbname, parseError := parseURL(config, url) + if err != nil { + return nil, parseError + } + if len(port) == 0 { + port = "1433" + } + db, err := sql.Open(driver, ("server=" + server + ";user id=" + username + ";password=" + password + ";port=" + port + ";database=" + dbname + ";encrypt=true;TrustServerCertificate=true")) + if db != nil { + defer db.Close() + } + if err != nil { + return nil, err + } + + return coreopts.BuildOptions.ActiveSessions(db) +} + +func parseURL(config *core.CoreConfig, url string) (string, string, string, string, error) { + //only works with jdbc:mysql or jdbc:sqlserver. + regex := regexp.MustCompile(`(?i)(mysql|sqlserver)://([\w\-\.]+)(?::(\d{0,5}))?(?:/|.*;DatabaseName=)(\w+).*`) + m := regex.FindStringSubmatch(url) + if m == nil { + parseFailureErr := errors.New("incorrect URL format") + eUtils.LogErrorMessage(config, parseFailureErr.Error(), true) + return "", "", "", "", parseFailureErr + } + return m[1], m[2], m[3], m[4], nil +} + +func (s *Server) getVaultSessions(env string) ([]map[string]interface{}, error) { + mod, err := helperkv.NewModifier(false, s.VaultToken, s.VaultAddr, "nonprod", nil, true, s.Log) + if err != nil { + return nil, err + } + mod.Env = "" + + sessions := []map[string]interface{}{} + paths, err := mod.List("apiLogins/"+env, s.Log) + if paths == nil { + return nil, fmt.Errorf("Nothing found under apiLogins/" + env) + } + if err != nil { + return nil, err + } + mod.Env = env + + // Pass through all registered users + var id int + if users, ok := paths.Data["keys"].([]interface{}); ok { + for _, user := range users { + if user == "meta" { + continue + } + userData, err := mod.ReadData("apiLogins/" + user.(string)) + if err != nil { + return nil, err + } + + issued, err := userData["Issued"].(json.Number).Int64() + if err != nil { + return nil, err + } + expires, err := userData["Expires"].(json.Number).Int64() + if err != nil { + return nil, err + } + // Check if session has expired + if expires < time.Now().Unix() { + userData["Issued"] = -1 + userData["Expires"] = -1 + } else { + sessions = append(sessions, map[string]interface{}{ + "ID": id, + "User": strings.TrimSpace(user.(string)), + "LastLogIn": issued, + }) + id++ + } + } + } + + return sessions, nil +} diff --git a/trcweb/server/sessionq.go b/trcweb/server/sessionq.go index 84de97346..d64ff374a 100644 --- a/trcweb/server/sessionq.go +++ b/trcweb/server/sessionq.go @@ -1,86 +1,86 @@ -package server - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - - "github.com/trimble-oss/tierceron/buildopts/coreopts" - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" -) - -// ProxyLogin proxy logs in the user. -func ProxyLogin(config *core.CoreConfig, authHost string, req *pb.LoginReq) (string, string, *pb.LoginResp, error) { - credentials := bytes.NewBuffer([]byte{}) - - err := json.NewEncoder(credentials).Encode(map[string]string{ - "username": req.Username, - "password": req.Password, - }) - - if err != nil { - eUtils.LogErrorObject(config, err, false) - return "", "", nil, err - } - - client := &http.Client{} - res, err := client.Post(authHost, "application/json", credentials) - - if err != nil { - eUtils.LogErrorObject(config, err, false) - return "", "", nil, err - } - - switch res.StatusCode { - case 401: - return "", "", &pb.LoginResp{ - Success: false, - AuthToken: "", - }, nil - case 200: - fallthrough - case 204: - var response map[string]interface{} - bodyBytes, err := io.ReadAll(res.Body) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return "", "", nil, err - } - - err = json.Unmarshal([]byte(bodyBytes), &response) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return "", "", nil, err - } - - if userNameField, ok := response[coreopts.BuildOptions.GetUserNameField()].(string); ok { - if userCodeField, ok := response[coreopts.BuildOptions.GetUserCodeField()].(string); ok { - - return userNameField, userCodeField, &pb.LoginResp{ - Success: true, - AuthToken: "", - }, nil - } - err = fmt.Errorf("unable to parse userCodeField in auth response") - eUtils.LogErrorObject(config, err, false) - } else { - err = fmt.Errorf("unable to parse userNameField in auth response") - eUtils.LogErrorObject(config, err, false) - } - - return "", "", &pb.LoginResp{ - Success: false, - AuthToken: "", - }, err - } - err = fmt.Errorf("unexpected response code from auth endpoint: %d", res.StatusCode) - eUtils.LogErrorObject(config, err, false) - return "", "", &pb.LoginResp{ - Success: false, - AuthToken: "", - }, nil -} +package server + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/trimble-oss/tierceron/buildopts/coreopts" + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" +) + +// ProxyLogin proxy logs in the user. +func ProxyLogin(config *core.CoreConfig, authHost string, req *pb.LoginReq) (string, string, *pb.LoginResp, error) { + credentials := bytes.NewBuffer([]byte{}) + + err := json.NewEncoder(credentials).Encode(map[string]string{ + "username": req.Username, + "password": req.Password, + }) + + if err != nil { + eUtils.LogErrorObject(config, err, false) + return "", "", nil, err + } + + client := &http.Client{} + res, err := client.Post(authHost, "application/json", credentials) + + if err != nil { + eUtils.LogErrorObject(config, err, false) + return "", "", nil, err + } + + switch res.StatusCode { + case 401: + return "", "", &pb.LoginResp{ + Success: false, + AuthToken: "", + }, nil + case 200: + fallthrough + case 204: + var response map[string]interface{} + bodyBytes, err := io.ReadAll(res.Body) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return "", "", nil, err + } + + err = json.Unmarshal([]byte(bodyBytes), &response) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return "", "", nil, err + } + + if userNameField, ok := response[coreopts.BuildOptions.GetUserNameField()].(string); ok { + if userCodeField, ok := response[coreopts.BuildOptions.GetUserCodeField()].(string); ok { + + return userNameField, userCodeField, &pb.LoginResp{ + Success: true, + AuthToken: "", + }, nil + } + err = fmt.Errorf("unable to parse userCodeField in auth response") + eUtils.LogErrorObject(config, err, false) + } else { + err = fmt.Errorf("unable to parse userNameField in auth response") + eUtils.LogErrorObject(config, err, false) + } + + return "", "", &pb.LoginResp{ + Success: false, + AuthToken: "", + }, err + } + err = fmt.Errorf("unexpected response code from auth endpoint: %d", res.StatusCode) + eUtils.LogErrorObject(config, err, false) + return "", "", &pb.LoginResp{ + Success: false, + AuthToken: "", + }, nil +} diff --git a/trcweb/server/sessions.go b/trcweb/server/sessions.go index 06bb61142..ad857337a 100644 --- a/trcweb/server/sessions.go +++ b/trcweb/server/sessions.go @@ -1,3 +1,3 @@ -package server - -// TODO -- wipe me. +package server + +// TODO -- wipe me. diff --git a/trcweb/server/tokens.go b/trcweb/server/tokens.go index 88ae56099..6c9425c35 100644 --- a/trcweb/server/tokens.go +++ b/trcweb/server/tokens.go @@ -1,124 +1,124 @@ -package server - -import ( - "context" - "fmt" - "time" - - jwt "github.com/golang-jwt/jwt" - - "github.com/trimble-oss/tierceron/pkg/core" - eUtils "github.com/trimble-oss/tierceron/pkg/utils" - helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" - sys "github.com/trimble-oss/tierceron/pkg/vaulthelper/system" - pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" -) - -func (s *Server) generateJWT(user string, id string, mod *helperkv.Modifier) (string, error) { - tokenSecret := s.TrcAPITokenSecret - currentTime := time.Now().Unix() - expTime := currentTime + 24*60*60 - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "sub": id, - "name": user, - "iss": "Viewpoint, Inc.", - "aud": "Viewpoint Vault WebAPI", - "iat": currentTime, - "exp": expTime, - }) - - // Upload token information to vault - if mod != nil { - defer func() { - tokenData := map[string]interface{}{ - "ID": id, - "Issued": currentTime, - "Expires": expTime, - } - warn, err := mod.Write("apiLogins/"+user, tokenData, config.Log) - eUtils.LogWarningsObject(config, warn, false) - eUtils.LogErrorObject(config, err, false) - }() - } - - return token.SignedString(tokenSecret) -} - -// GetVaultTokens takes app role credentials and attempts to fetch names tokens from the vault -func (s *Server) GetVaultTokens(ctx context.Context, req *pb.TokensReq) (*pb.TokensResp, error) { - // Create 2 vault connections, one for checking/rolling tokens, the other for accessing the AWS user cubbyhole - v, err := sys.NewVault(false, s.VaultAddr, "nonprod", false, false, false, s.Log) - config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} - if v != nil { - defer v.Close() - } - - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - v.SetToken(s.VaultToken) - - if len(req.AppRoleID) == 0 || len(req.AppRoleSecretID) == 0 { - return nil, fmt.Errorf("need both role ID and secret ID to login through app role") - } - - arToken, err := v.AppRoleLogin(req.AppRoleID, req.AppRoleSecretID) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - // Modifier to access token values granted to bamboo - mod, err := helperkv.NewModifier(false, arToken, s.VaultAddr, "nonprod", nil, true, s.Log) - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - mod.EnvBasis = "bamboo" - mod.Env = "bamboo" - - data, err := mod.ReadData("super-secrets/tokens") - if err != nil { - eUtils.LogErrorObject(config, err, false) - return nil, err - } - - reqTokens := make(map[string]bool, len(req.Tokens)) - for _, k := range req.Tokens { - reqTokens[k] = true - } - - tokens := []*pb.TokensResp_Token{} - - for k, v := range data { - if token, ok := v.(string); ok { - if len(reqTokens) == 0 || reqTokens[k] { - tokens = append(tokens, &pb.TokensResp_Token{ - Name: k, - Value: token, - }) - } - } else { - eUtils.LogWarningsObject(config, []string{fmt.Sprintf("Failed to convert token %s to string", k)}, false) - } - } - // AWS - // Get tokens from cubbyhole - - // TOKEN ROLLER - // Check state of tokens, reroll tokens within 1h of expiration - - // AWS - // Store newly rolled tokens - - return &pb.TokensResp{Tokens: tokens}, nil -} - -// RollTokens checks the validity of tokens in super-secrets/bamboo/tokens and rerolls them -func (s *Server) RollTokens(ctx context.Context, req *pb.NoParams) (*pb.NoParams, error) { - return &pb.NoParams{}, nil -} +package server + +import ( + "context" + "fmt" + "time" + + jwt "github.com/golang-jwt/jwt" + + "github.com/trimble-oss/tierceron/pkg/core" + eUtils "github.com/trimble-oss/tierceron/pkg/utils" + helperkv "github.com/trimble-oss/tierceron/pkg/vaulthelper/kv" + sys "github.com/trimble-oss/tierceron/pkg/vaulthelper/system" + pb "github.com/trimble-oss/tierceron/trcweb/rpc/apinator" +) + +func (s *Server) generateJWT(user string, id string, mod *helperkv.Modifier) (string, error) { + tokenSecret := s.TrcAPITokenSecret + currentTime := time.Now().Unix() + expTime := currentTime + 24*60*60 + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": id, + "name": user, + "iss": "Viewpoint, Inc.", + "aud": "Viewpoint Vault WebAPI", + "iat": currentTime, + "exp": expTime, + }) + + // Upload token information to vault + if mod != nil { + defer func() { + tokenData := map[string]interface{}{ + "ID": id, + "Issued": currentTime, + "Expires": expTime, + } + warn, err := mod.Write("apiLogins/"+user, tokenData, config.Log) + eUtils.LogWarningsObject(config, warn, false) + eUtils.LogErrorObject(config, err, false) + }() + } + + return token.SignedString(tokenSecret) +} + +// GetVaultTokens takes app role credentials and attempts to fetch names tokens from the vault +func (s *Server) GetVaultTokens(ctx context.Context, req *pb.TokensReq) (*pb.TokensResp, error) { + // Create 2 vault connections, one for checking/rolling tokens, the other for accessing the AWS user cubbyhole + v, err := sys.NewVault(false, s.VaultAddr, "nonprod", false, false, false, s.Log) + config := &core.CoreConfig{ExitOnFailure: false, Log: s.Log} + if v != nil { + defer v.Close() + } + + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + v.SetToken(s.VaultToken) + + if len(req.AppRoleID) == 0 || len(req.AppRoleSecretID) == 0 { + return nil, fmt.Errorf("need both role ID and secret ID to login through app role") + } + + arToken, err := v.AppRoleLogin(req.AppRoleID, req.AppRoleSecretID) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + // Modifier to access token values granted to bamboo + mod, err := helperkv.NewModifier(false, arToken, s.VaultAddr, "nonprod", nil, true, s.Log) + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + mod.EnvBasis = "bamboo" + mod.Env = "bamboo" + + data, err := mod.ReadData("super-secrets/tokens") + if err != nil { + eUtils.LogErrorObject(config, err, false) + return nil, err + } + + reqTokens := make(map[string]bool, len(req.Tokens)) + for _, k := range req.Tokens { + reqTokens[k] = true + } + + tokens := []*pb.TokensResp_Token{} + + for k, v := range data { + if token, ok := v.(string); ok { + if len(reqTokens) == 0 || reqTokens[k] { + tokens = append(tokens, &pb.TokensResp_Token{ + Name: k, + Value: token, + }) + } + } else { + eUtils.LogWarningsObject(config, []string{fmt.Sprintf("Failed to convert token %s to string", k)}, false) + } + } + // AWS + // Get tokens from cubbyhole + + // TOKEN ROLLER + // Check state of tokens, reroll tokens within 1h of expiration + + // AWS + // Store newly rolled tokens + + return &pb.TokensResp{Tokens: tokens}, nil +} + +// RollTokens checks the validity of tokens in super-secrets/bamboo/tokens and rerolls them +func (s *Server) RollTokens(ctx context.Context, req *pb.NoParams) (*pb.NoParams, error) { + return &pb.NoParams{}, nil +}