github.com/actions-on-google/gactions@v3.2.0+incompatible/cmd/gactions/cli/decrypt/decrypt.go (about) 1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package decrypt provides an implementation of "gactions decrypt" command. 16 package decrypt 17 18 import ( 19 "context" 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "runtime" 25 "strings" 26 27 "github.com/actions-on-google/gactions/api/sdk" 28 "github.com/actions-on-google/gactions/log" 29 "github.com/actions-on-google/gactions/project" 30 "github.com/spf13/cobra" 31 "gopkg.in/yaml.v2" 32 ) 33 34 func parseClientSecret(files map[string][]byte) (string, error) { 35 type secretFile struct { 36 EncryptedClientSecret string `yaml:"encryptedClientSecret"` 37 } 38 in, ok := files["settings/accountLinkingSecret.yaml"] 39 if !ok { 40 log.Infoln("accountLinkingSecret.yaml not found in project files") 41 return "", errors.New("accountLinkingSecret.yaml not found in project files. " + 42 "Try encrypting your client secret first, or pulling an existing project with a client secret") 43 } 44 f := secretFile{} 45 if err := yaml.Unmarshal(in, &f); err != nil { 46 return "", err 47 } 48 return f.EncryptedClientSecret, nil 49 } 50 51 func expandTilde(p string) (string, error) { 52 if !strings.HasPrefix(p, "~/") || runtime.GOOS == "windows" { 53 return p, nil 54 } 55 home, err := os.UserHomeDir() 56 if err != nil { 57 return p, err 58 } 59 return filepath.Join(home, p[2:]), nil 60 } 61 62 func normPath(p, root string) string { 63 norm := filepath.FromSlash(p) 64 if strings.HasPrefix(p, "~/") { 65 t, err := expandTilde(p) 66 if err == nil { 67 norm = t 68 } 69 } 70 if strings.HasPrefix(p, "./") || strings.HasPrefix(p, "../") || strings.HasPrefix(p, ".\\") || strings.HasPrefix(p, "..\\") { 71 norm = filepath.Clean(filepath.Join(root, p)) 72 } 73 if !filepath.IsAbs(norm) { 74 norm = filepath.Join(root, p) 75 } 76 return norm 77 } 78 79 // AddCommand adds decrypt sub-command to the passed in root command. 80 func AddCommand(ctx context.Context, root *cobra.Command, proj project.Project) { 81 decrypt := &cobra.Command{ 82 Use: "decrypt <plaint-text-file>", 83 Short: "Decrypt client secret.", 84 Long: "This command decrypts the client secret key used in Account Linking. Specify a file path for the decrypt output. This can be a relative or absolute path.", 85 RunE: func(cmd *cobra.Command, args []string) error { 86 if proj.ProjectRoot() == "" { 87 log.Errorf(`Can't find a project root. This may be because (1) %q was not found in this or any of the parent folders, or (2) if %q was found, but the key "sdkPath" was missing, or (3) if %q and manifest.yaml were both not found.`, project.ConfigName, project.ConfigName, project.ConfigName) 88 return errors.New("can not determine project root") 89 } 90 files, err := proj.Files() 91 if err != nil { 92 return err 93 } 94 s, err := parseClientSecret(files) 95 if err != nil { 96 return err 97 } 98 out := normPath(args[0], proj.ProjectRoot()) 99 return sdk.DecryptSecretJSON(ctx, proj, s, out) 100 }, 101 Args: func(cmd *cobra.Command, args []string) error { 102 if len(args) > 1 { 103 return fmt.Errorf("unexpected arguments: %v", args) 104 } 105 if len(args) < 1 { 106 return fmt.Errorf(`<plain-text-file> argument is missing. Try "gactions decrypt <pathToPlainTextFile>"`) 107 } 108 return nil 109 }, 110 } 111 root.AddCommand(decrypt) 112 }