forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
request_token.go
122 lines (102 loc) · 3.23 KB
/
request_token.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package tokencmd
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"github.com/RangelReale/osincli"
"github.com/golang/glog"
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/openshift/origin/pkg/client"
server "github.com/openshift/origin/pkg/cmd/server/origin"
"github.com/openshift/origin/pkg/oauth/server/osinserver"
)
// RequestToken uses the cmd arguments to locate an openshift oauth server and attempts to authenticate
// it returns the access token if it gets one. An error if it does not
func RequestToken(clientCfg *kclient.Config, reader io.Reader, defaultUsername string, defaultPassword string) (string, error) {
tokenGetter := &tokenGetterInfo{}
osClient, err := client.New(clientCfg)
if err != nil {
return "", err
}
// get the transport, so that we can use it to build our own client that wraps it
// our client understands certain challenges and can respond to them
clientTransport, err := kclient.TransportFor(clientCfg)
if err != nil {
return "", err
}
httpClient := &http.Client{
Transport: clientTransport,
CheckRedirect: tokenGetter.checkRedirect,
}
osClient.Client = &challengingClient{httpClient, reader, defaultUsername, defaultPassword}
result := osClient.Get().AbsPath(server.OpenShiftOAuthAPIPrefix, osinserver.AuthorizePath).
Param("response_type", "token").
Param("client_id", "openshift-challenging-client").
Do()
if err := result.Error(); err != nil && !isRedirectError(err) {
return "", err
}
if len(tokenGetter.accessToken) == 0 {
r, _ := result.Raw()
if description, ok := rawOAuthJSONErrorDescription(r); ok {
return "", fmt.Errorf("cannot retrieve a token: %s", description)
}
glog.V(4).Infof("A request token could not be created, server returned: %s", string(r))
return "", fmt.Errorf("the server did not return a token (possible server error)")
}
return tokenGetter.accessToken, nil
}
func rawOAuthJSONErrorDescription(data []byte) (string, bool) {
output := osincli.ResponseData{}
decoder := json.NewDecoder(bytes.NewBuffer(data))
if err := decoder.Decode(&output); err != nil {
return "", false
}
if _, ok := output["error"]; !ok {
return "", false
}
desc, ok := output["error_description"]
if !ok {
return "", false
}
s, ok := desc.(string)
if !ok || len(s) == 0 {
return "", false
}
return s, true
}
const accessTokenKey = "access_token"
var errRedirectComplete = errors.New("found access token")
type tokenGetterInfo struct {
accessToken string
}
// checkRedirect watches the redirects to see if any contain the access_token anchor. It then stores the value of the access token for later retrieval
func (tokenGetter *tokenGetterInfo) checkRedirect(req *http.Request, via []*http.Request) error {
fragment := req.URL.Fragment
if values, err := url.ParseQuery(fragment); err == nil {
if v, ok := values[accessTokenKey]; ok {
if len(v) > 0 {
tokenGetter.accessToken = v[0]
}
return errRedirectComplete
}
}
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
func isRedirectError(err error) bool {
if err == errRedirectComplete {
return true
}
switch t := err.(type) {
case *url.Error:
return t.Err == errRedirectComplete
}
return false
}