github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/auth/gitcredential/helper.go (about) 1 package login 2 3 import ( 4 "bufio" 5 "fmt" 6 "net/url" 7 "strings" 8 9 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 10 "github.com/ungtb10d/cli/v2/pkg/iostreams" 11 "github.com/spf13/cobra" 12 ) 13 14 const tokenUser = "x-access-token" 15 16 type config interface { 17 AuthToken(string) (string, string) 18 Get(string, string) (string, error) 19 } 20 21 type CredentialOptions struct { 22 IO *iostreams.IOStreams 23 Config func() (config, error) 24 25 Operation string 26 } 27 28 func NewCmdCredential(f *cmdutil.Factory, runF func(*CredentialOptions) error) *cobra.Command { 29 opts := &CredentialOptions{ 30 IO: f.IOStreams, 31 Config: func() (config, error) { 32 return f.Config() 33 }, 34 } 35 36 cmd := &cobra.Command{ 37 Use: "git-credential", 38 Args: cobra.ExactArgs(1), 39 Short: "Implements git credential helper protocol", 40 Hidden: true, 41 RunE: func(cmd *cobra.Command, args []string) error { 42 opts.Operation = args[0] 43 44 if runF != nil { 45 return runF(opts) 46 } 47 return helperRun(opts) 48 }, 49 } 50 51 return cmd 52 } 53 54 func helperRun(opts *CredentialOptions) error { 55 if opts.Operation == "store" { 56 // We pretend to implement the "store" operation, but do nothing since we already have a cached token. 57 return cmdutil.SilentError 58 } 59 60 if opts.Operation != "get" { 61 return fmt.Errorf("gh auth git-credential: %q operation not supported", opts.Operation) 62 } 63 64 wants := map[string]string{} 65 66 s := bufio.NewScanner(opts.IO.In) 67 for s.Scan() { 68 line := s.Text() 69 if line == "" { 70 break 71 } 72 parts := strings.SplitN(line, "=", 2) 73 if len(parts) < 2 { 74 continue 75 } 76 key, value := parts[0], parts[1] 77 if key == "url" { 78 u, err := url.Parse(value) 79 if err != nil { 80 return err 81 } 82 wants["protocol"] = u.Scheme 83 wants["host"] = u.Host 84 wants["path"] = u.Path 85 wants["username"] = u.User.Username() 86 wants["password"], _ = u.User.Password() 87 } else { 88 wants[key] = value 89 } 90 } 91 if err := s.Err(); err != nil { 92 return err 93 } 94 95 if wants["protocol"] != "https" { 96 return cmdutil.SilentError 97 } 98 99 cfg, err := opts.Config() 100 if err != nil { 101 return err 102 } 103 104 lookupHost := wants["host"] 105 var gotUser string 106 gotToken, source := cfg.AuthToken(lookupHost) 107 if gotToken == "" && strings.HasPrefix(lookupHost, "gist.") { 108 lookupHost = strings.TrimPrefix(lookupHost, "gist.") 109 gotToken, source = cfg.AuthToken(lookupHost) 110 } 111 112 if strings.HasSuffix(source, "_TOKEN") { 113 gotUser = tokenUser 114 } else { 115 gotUser, _ = cfg.Get(lookupHost, "user") 116 if gotUser == "" { 117 gotUser = tokenUser 118 } 119 } 120 121 if gotUser == "" || gotToken == "" { 122 return cmdutil.SilentError 123 } 124 125 if wants["username"] != "" && gotUser != tokenUser && !strings.EqualFold(wants["username"], gotUser) { 126 return cmdutil.SilentError 127 } 128 129 fmt.Fprint(opts.IO.Out, "protocol=https\n") 130 fmt.Fprintf(opts.IO.Out, "host=%s\n", wants["host"]) 131 fmt.Fprintf(opts.IO.Out, "username=%s\n", gotUser) 132 fmt.Fprintf(opts.IO.Out, "password=%s\n", gotToken) 133 134 return nil 135 }