1
2
3 package auth
4
5 import (
6 "fmt"
7 "os"
8 "os/exec"
9 "strings"
10
11 "github.com/cli/go-gh/v2/internal/set"
12 "github.com/cli/go-gh/v2/pkg/config"
13 "github.com/cli/safeexec"
14 )
15
16 const (
17 codespaces = "CODESPACES"
18 defaultSource = "default"
19 ghEnterpriseToken = "GH_ENTERPRISE_TOKEN"
20 ghHost = "GH_HOST"
21 ghToken = "GH_TOKEN"
22 github = "github.com"
23 githubEnterpriseToken = "GITHUB_ENTERPRISE_TOKEN"
24 githubToken = "GITHUB_TOKEN"
25 hostsKey = "hosts"
26 localhost = "github.localhost"
27 oauthToken = "oauth_token"
28 tenancyHost = "ghe.com"
29 )
30
31
32
33
34
35
36 func TokenForHost(host string) (string, string) {
37 if token, source := TokenFromEnvOrConfig(host); token != "" {
38 return token, source
39 }
40
41 ghExe := os.Getenv("GH_PATH")
42 if ghExe == "" {
43 ghExe, _ = safeexec.LookPath("gh")
44 }
45
46 if ghExe != "" {
47 if token, source := tokenFromGh(ghExe, host); token != "" {
48 return token, source
49 }
50 }
51
52 return "", defaultSource
53 }
54
55
56
57
58 func TokenFromEnvOrConfig(host string) (string, string) {
59 cfg, _ := config.Read(nil)
60 return tokenForHost(cfg, host)
61 }
62
63 func tokenForHost(cfg *config.Config, host string) (string, string) {
64 normalizedHost := NormalizeHostname(host)
65
66
67
68 if normalizedHost == github || IsTenancy(normalizedHost) || normalizedHost == localhost {
69 if token := os.Getenv(ghToken); token != "" {
70 return token, ghToken
71 }
72
73 if token := os.Getenv(githubToken); token != "" {
74 return token, githubToken
75 }
76 } else {
77 if token := os.Getenv(ghEnterpriseToken); token != "" {
78 return token, ghEnterpriseToken
79 }
80
81 if token := os.Getenv(githubEnterpriseToken); token != "" {
82 return token, githubEnterpriseToken
83 }
84 }
85
86
87
88
89
90 if cfg == nil {
91 return "", defaultSource
92 }
93
94 token, err := cfg.Get([]string{hostsKey, normalizedHost, oauthToken})
95 if err != nil {
96 return "", defaultSource
97 }
98
99 return token, oauthToken
100 }
101
102 func tokenFromGh(path string, host string) (string, string) {
103 cmd := exec.Command(path, "auth", "token", "--secure-storage", "--hostname", host)
104 result, err := cmd.Output()
105 if err != nil {
106 return "", "gh"
107 }
108 return strings.TrimSpace(string(result)), "gh"
109 }
110
111
112
113
114
115 func KnownHosts() []string {
116 cfg, _ := config.Read(nil)
117 return knownHosts(cfg)
118 }
119
120 func knownHosts(cfg *config.Config) []string {
121 hosts := set.NewStringSet()
122 if host := os.Getenv(ghHost); host != "" {
123 hosts.Add(host)
124 }
125 if token, _ := tokenForHost(cfg, github); token != "" {
126 hosts.Add(github)
127 }
128 if cfg != nil {
129 keys, err := cfg.Keys([]string{hostsKey})
130 if err == nil {
131 hosts.AddValues(keys)
132 }
133 }
134 return hosts.ToSlice()
135 }
136
137
138
139
140
141 func DefaultHost() (string, string) {
142 cfg, _ := config.Read(nil)
143 return defaultHost(cfg)
144 }
145
146 func defaultHost(cfg *config.Config) (string, string) {
147 if host := os.Getenv(ghHost); host != "" {
148 return host, ghHost
149 }
150 if cfg != nil {
151 keys, err := cfg.Keys([]string{hostsKey})
152 if err == nil && len(keys) == 1 {
153 return keys[0], hostsKey
154 }
155 }
156 return github, defaultSource
157 }
158
159
160
161 func IsEnterprise(host string) bool {
162
163
164 normalizedHost := NormalizeHostname(host)
165 return normalizedHost != github && normalizedHost != localhost && !IsTenancy(normalizedHost)
166 }
167
168
169
170 func IsTenancy(host string) bool {
171 normalizedHost := NormalizeHostname(host)
172 return strings.HasSuffix(normalizedHost, "."+tenancyHost)
173 }
174
175
176
177
178 func NormalizeHostname(host string) string {
179 hostname := strings.ToLower(host)
180 if strings.HasSuffix(hostname, "."+github) {
181 return github
182 }
183 if strings.HasSuffix(hostname, "."+localhost) {
184 return localhost
185 }
186
187
188
189
190 if before, found := cutSuffix(hostname, "."+tenancyHost); found {
191 idx := strings.LastIndex(before, ".")
192 return fmt.Sprintf("%s.%s", before[idx+1:], tenancyHost)
193 }
194 return hostname
195 }
196
197
198 func cutSuffix(s, suffix string) (string, bool) {
199 if !strings.HasSuffix(s, suffix) {
200 return s, false
201 }
202 return s[:len(s)-len(suffix)], true
203 }
204
View as plain text