GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( fe6920...0b968c )
by
unknown
06:56
created

providers.*OAuthCredentialsProvider.GetCredentials   B

Complexity

Conditions 7

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nop 0
dl 0
loc 32
rs 7.9759
c 0
b 0
f 0
1
package providers
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
	"net/http"
8
	"net/url"
9
	"time"
10
11
	httputil "github.com/aliyun/credentials-go/credentials/internal/http"
12
	"github.com/aliyun/credentials-go/credentials/internal/utils"
13
)
14
15
// OAuthTokenUpdateCallback 定义OAuth令牌更新回调函数类型
16
type OAuthTokenUpdateCallback func(refreshToken, accessToken, accessKey, secret, securityToken string, accessTokenExpire, stsExpire int64) error
17
18
type oauthCredentialResponse struct {
19
	AccessKeyId     string `json:"accessKeyId"`
20
	AccessKeySecret string `json:"accessKeySecret"`
21
	SecurityToken   string `json:"securityToken"`
22
	Expiration      string `json:"expiration"`
23
	RequestId       string `json:"requestId"`
24
}
25
26
type oauthRefreshTokenResponse struct {
27
	AccessToken  string `json:"access_token"`
28
	RefreshToken string `json:"refresh_token"`
29
	ExpiresIn    int64  `json:"expires_in"`
30
	TokenType    string `json:"token_type"`
31
}
32
33
type OAuthCredentialsProvider struct {
34
	clientId          string
35
	signInUrl         string
36
	refreshToken      string
37
	accessToken       string
38
	accessTokenExpire int64
39
40
	lastUpdateTimestamp int64
41
	expirationTimestamp int64
42
	sessionCredentials  *sessionCredentials
43
	// for http options
44
	httpOptions *HttpOptions
45
	// OAuth token call back
46
	tokenUpdateCallback OAuthTokenUpdateCallback
47
}
48
49
type OAuthCredentialsProviderBuilder struct {
50
	provider *OAuthCredentialsProvider
51
}
52
53
func NewOAuthCredentialsProviderBuilder() *OAuthCredentialsProviderBuilder {
54
	return &OAuthCredentialsProviderBuilder{
55
		provider: &OAuthCredentialsProvider{},
56
	}
57
}
58
59
func (b *OAuthCredentialsProviderBuilder) WithClientId(clientId string) *OAuthCredentialsProviderBuilder {
60
	b.provider.clientId = clientId
61
	return b
62
}
63
64
func (b *OAuthCredentialsProviderBuilder) WithSignInUrl(signInUrl string) *OAuthCredentialsProviderBuilder {
65
	b.provider.signInUrl = signInUrl
66
	return b
67
}
68
69
func (b *OAuthCredentialsProviderBuilder) WithRefreshToken(refreshToken string) *OAuthCredentialsProviderBuilder {
70
	b.provider.refreshToken = refreshToken
71
	return b
72
}
73
74
func (b *OAuthCredentialsProviderBuilder) WithAccessToken(accessToken string) *OAuthCredentialsProviderBuilder {
75
	b.provider.accessToken = accessToken
76
	return b
77
}
78
79
func (b *OAuthCredentialsProviderBuilder) WithAccessTokenExpire(accessTokenExpire int64) *OAuthCredentialsProviderBuilder {
80
	b.provider.accessTokenExpire = accessTokenExpire
81
	return b
82
}
83
84
func (b *OAuthCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *OAuthCredentialsProviderBuilder {
85
	b.provider.httpOptions = httpOptions
86
	return b
87
}
88
89
func (b *OAuthCredentialsProviderBuilder) WithTokenUpdateCallback(callback OAuthTokenUpdateCallback) *OAuthCredentialsProviderBuilder {
90
	b.provider.tokenUpdateCallback = callback
91
	return b
92
}
93
94
func (b *OAuthCredentialsProviderBuilder) Build() (provider *OAuthCredentialsProvider, err error) {
95
	if b.provider.clientId == "" {
96
		err = errors.New("the ClientId is empty")
97
		return
98
	}
99
100
	if b.provider.signInUrl == "" {
101
		err = errors.New("the url for sign-in is empty")
102
		return
103
	}
104
105
	if b.provider.refreshToken == "" {
106
		err = errors.New("OAuth access token is empty or expired, please re-login with cli")
107
		return
108
	}
109
110
	provider = b.provider
111
	return
112
}
113
114
func (provider *OAuthCredentialsProvider) getCredentials() (session *sessionCredentials, err error) {
115
116
	if provider.accessToken == "" || provider.accessTokenExpire == 0 || provider.accessTokenExpire-time.Now().Unix() <= 180 {
117
		err = provider.tryRefreshOauthToken()
118
		if err != nil {
119
			return nil, err
120
		}
121
	}
122
123
	url, err := url.Parse(provider.signInUrl)
124
	if err != nil {
125
		return nil, err
126
	}
127
128
	req := &httputil.Request{
129
		Method:   "POST",
130
		Protocol: url.Scheme,
131
		Host:     url.Host,
132
		Path:     "/v1/exchange",
133
		Headers:  map[string]string{},
134
	}
135
136
	connectTimeout := 5 * time.Second
137
	readTimeout := 10 * time.Second
138
139
	if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
140
		connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
141
	}
142
	if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
143
		readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
144
	}
145
	if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
146
		req.Proxy = provider.httpOptions.Proxy
147
	}
148
	req.ConnectTimeout = connectTimeout
149
	req.ReadTimeout = readTimeout
150
151
	// set headers
152
	req.Headers["Content-Type"] = "application/json"
153
	req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", provider.accessToken)
154
	res, err := httpDo(req)
155
	if err != nil {
156
		return
157
	}
158
159
	if res.StatusCode != http.StatusOK {
160
		message := "get session token from OAuth failed: "
161
		err = errors.New(message + string(res.Body))
162
		return
163
	}
164
	var data oauthCredentialResponse
165
	err = json.Unmarshal(res.Body, &data)
166
	if err != nil {
167
		err = fmt.Errorf("get session token from OAuth failed, json.Unmarshal fail: %s", err.Error())
168
		return
169
	}
170
171
	if data.AccessKeyId == "" || data.AccessKeySecret == "" || data.SecurityToken == "" {
172
		err = fmt.Errorf("refresh session token err, fail to get credentials from OAuth: " + string(res.Body))
173
		return
174
	}
175
176
	session = &sessionCredentials{
177
		AccessKeyId:     data.AccessKeyId,
178
		AccessKeySecret: data.AccessKeySecret,
179
		SecurityToken:   data.SecurityToken,
180
		Expiration:      data.Expiration,
181
	}
182
	return
183
}
184
185
func (provider *OAuthCredentialsProvider) tryRefreshOauthToken() (err error) {
186
	refreshToken := provider.refreshToken
187
	clientId := provider.clientId
188
189
	url, err := url.Parse(provider.signInUrl)
190
	if err != nil {
191
		return
192
	}
193
194
	req := &httputil.Request{
195
		Method:   "POST",
196
		Protocol: url.Scheme,
197
		Host:     url.Host,
198
		Path:     "/v1/token",
199
		Headers:  map[string]string{},
200
	}
201
202
	connectTimeout := 5 * time.Second
203
	readTimeout := 10 * time.Second
204
205
	if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
206
		connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
207
	}
208
	if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
209
		readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
210
	}
211
	if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
212
		req.Proxy = provider.httpOptions.Proxy
213
	}
214
	req.ConnectTimeout = connectTimeout
215
	req.ReadTimeout = readTimeout
216
217
	bodyForm := make(map[string]string)
218
	bodyForm["grant_type"] = "refresh_token"
219
	bodyForm["refresh_token"] = refreshToken
220
	bodyForm["client_id"] = clientId
221
	bodyForm["Timestamp"] = utils.GetTimeInFormatISO8601()
222
	req.Form = bodyForm
223
224
	req.Headers["Content-Type"] = "application/x-www-form-urlencoded"
225
	resp, err := httpDo(req)
226
	if err != nil {
227
		return
228
	}
229
	if resp.StatusCode != http.StatusOK {
230
		return fmt.Errorf("failed to refresh token, status code: %d", resp.StatusCode)
231
	}
232
	var tokenResp oauthRefreshTokenResponse
233
	err = json.Unmarshal(resp.Body, &tokenResp)
234
	if err != nil {
235
		err = fmt.Errorf("get refresh token from OAuth failed, json.Unmarshal fail: %s", err.Error())
236
		return
237
	}
238
	if tokenResp.RefreshToken == "" || tokenResp.AccessToken == "" {
239
		err = fmt.Errorf("failed to refresh token from OAuth: " + string(resp.Body))
240
		return
241
	}
242
	provider.accessToken = tokenResp.AccessToken
243
	provider.refreshToken = tokenResp.RefreshToken
244
	provider.accessTokenExpire = time.Now().Unix() + tokenResp.ExpiresIn
245
246
	return nil
247
}
248
249
func (provider *OAuthCredentialsProvider) needUpdateCredential() (result bool) {
250
	if provider.expirationTimestamp == 0 {
251
		return true
252
	}
253
254
	return provider.expirationTimestamp-time.Now().Unix() <= 180
255
}
256
257
func (provider *OAuthCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
258
	if provider.sessionCredentials == nil || provider.needUpdateCredential() {
259
		sessionCredentials, err1 := provider.getCredentials()
260
		if err1 != nil {
261
			return nil, err1
262
		}
263
264
		provider.sessionCredentials = sessionCredentials
265
		expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration)
266
		if err2 != nil {
267
			return nil, err2
268
		}
269
270
		provider.lastUpdateTimestamp = time.Now().Unix()
271
		provider.expirationTimestamp = expirationTime.Unix()
272
273
		// 如果设置了回调函数,则调用回调函数写回配置文件
274
		if provider.tokenUpdateCallback != nil {
275
			err1 := provider.tokenUpdateCallback(provider.refreshToken, provider.accessToken, sessionCredentials.AccessKeyId, sessionCredentials.AccessKeySecret, sessionCredentials.SecurityToken, provider.accessTokenExpire, provider.expirationTimestamp)
276
			if err1 != nil {
277
				fmt.Printf("Warning: failed to update OAuth tokens in config file: %v\n", err)
278
			}
279
		}
280
	}
281
282
	cc = &Credentials{
283
		AccessKeyId:     provider.sessionCredentials.AccessKeyId,
284
		AccessKeySecret: provider.sessionCredentials.AccessKeySecret,
285
		SecurityToken:   provider.sessionCredentials.SecurityToken,
286
		ProviderName:    provider.GetProviderName(),
287
	}
288
	return
289
}
290
291
func (provider *OAuthCredentialsProvider) GetProviderName() string {
292
	return "oauth"
293
}
294