@@ -5,19 +5,24 @@ import (
55 "html/template"
66 "io"
77 "net/http"
8+ "net/url"
89 "path"
910
1011 "github.com/RangelReale/osincli"
1112
1213 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314 utilruntime "k8s.io/apimachinery/pkg/util/runtime"
15+ "k8s.io/klog"
1416
1517 "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
1618 "github.com/openshift/origin/pkg/oauth/urls"
1719 "github.com/openshift/origin/pkg/oauthserver"
1820 "github.com/openshift/origin/pkg/oauthserver/authenticator/password/bootstrap"
21+ "github.com/openshift/origin/pkg/oauthserver/server/csrf"
1922)
2023
24+ const csrfParam = "csrf"
25+
2126type tokenRequest struct {
2227 publicMasterURL string
2328 // osinOAuthClientGetter is used to initialize osinOAuthClient.
@@ -27,14 +32,17 @@ type tokenRequest struct {
2732 // to check if we need the logout link for the bootstrap user
2833 tokens v1.OAuthAccessTokenInterface
2934 openShiftLogoutPrefix string
35+
36+ csrf csrf.CSRF
3037}
3138
32- func NewTokenRequest (publicMasterURL , openShiftLogoutPrefix string , osinOAuthClientGetter func () (* osincli.Client , error ), tokens v1.OAuthAccessTokenInterface ) oauthserver.Endpoints {
39+ func NewTokenRequest (publicMasterURL , openShiftLogoutPrefix string , osinOAuthClientGetter func () (* osincli.Client , error ), tokens v1.OAuthAccessTokenInterface , csrf csrf. CSRF ) oauthserver.Endpoints {
3340 return & tokenRequest {
3441 publicMasterURL : publicMasterURL ,
3542 osinOAuthClientGetter : osinOAuthClientGetter ,
3643 tokens : tokens ,
3744 openShiftLogoutPrefix : openShiftLogoutPrefix ,
45+ csrf : csrf ,
3846 }
3947}
4048
@@ -65,15 +73,47 @@ func (t *tokenRequest) requestToken(osinOAuthClient *osincli.Client, w http.Resp
6573}
6674
6775func (t * tokenRequest ) displayToken (osinOAuthClient * osincli.Client , w http.ResponseWriter , req * http.Request ) {
68- w .Header ().Set ("Content-Type" , "text/html; charset=UTF-8" )
69- requestURL := urls .OpenShiftOAuthTokenRequestURL ("" ) // relative url to token request endpoint
70- data := tokenData {RequestURL : requestURL , PublicMasterURL : t .publicMasterURL }
76+ switch req .Method {
77+ case http .MethodGet :
78+ t .displayTokenGet (osinOAuthClient , w , req )
79+ case http .MethodPost :
80+ t .displayTokenPost (osinOAuthClient , w , req )
81+ default :
82+ http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
83+ }
84+ }
7185
72- authorizeReq := osinOAuthClient .NewAuthorizeRequest (osincli .CODE )
73- authorizeData , err := authorizeReq .HandleRequest (req )
86+ func (t * tokenRequest ) displayTokenGet (osinOAuthClient * osincli.Client , w http.ResponseWriter , req * http.Request ) {
87+ data := formData {}
88+ authorizeData , ok := displayTokenStart (osinOAuthClient , w , req , & data .sharedData )
89+ if ! ok {
90+ renderForm (w , data )
91+ return
92+ }
93+
94+ uri , err := getBaseURL (req )
7495 if err != nil {
75- data .Error = fmt .Sprintf ("Error handling auth request: %v" , err )
76- w .WriteHeader (http .StatusInternalServerError )
96+ utilruntime .HandleError (fmt .Errorf ("unable to generate base URL: %v" , err ))
97+ http .Error (w , "Unable to determine URL" , http .StatusInternalServerError )
98+ return
99+ }
100+
101+ data .Action = uri .String ()
102+ data .Code = authorizeData .Code
103+ data .CSRF = t .csrf .Generate (w , req )
104+ renderForm (w , data )
105+ }
106+
107+ func (t * tokenRequest ) displayTokenPost (osinOAuthClient * osincli.Client , w http.ResponseWriter , req * http.Request ) {
108+ if ok := t .csrf .Check (req , req .FormValue (csrfParam )); ! ok {
109+ klog .V (4 ).Infof ("Invalid CSRF token: %s" , req .FormValue (csrfParam ))
110+ http .Error (w , "Could not check CSRF token. Please try again." , http .StatusBadRequest )
111+ return
112+ }
113+
114+ data := tokenData {PublicMasterURL : t .publicMasterURL }
115+ authorizeData , ok := displayTokenStart (osinOAuthClient , w , req , & data .sharedData )
116+ if ! ok {
77117 renderToken (w , data )
78118 return
79119 }
@@ -104,22 +144,66 @@ func (t *tokenRequest) displayToken(osinOAuthClient *osincli.Client, w http.Resp
104144 renderToken (w , data )
105145}
106146
147+ func displayTokenStart (osinOAuthClient * osincli.Client , w http.ResponseWriter , req * http.Request , data * sharedData ) (* osincli.AuthorizeData , bool ) {
148+ w .Header ().Set ("Content-Type" , "text/html; charset=UTF-8" )
149+
150+ requestURL := urls .OpenShiftOAuthTokenRequestURL ("" ) // relative url to token request endpoint
151+ data .RequestURL = requestURL // always set this field even on error cases
152+
153+ authorizeReq := osinOAuthClient .NewAuthorizeRequest (osincli .CODE )
154+ authorizeData , err := authorizeReq .HandleRequest (req )
155+ if err != nil {
156+ w .WriteHeader (http .StatusBadRequest )
157+ data .Error = fmt .Sprintf ("Error handling auth request: %v" , err )
158+ return nil , false
159+ }
160+
161+ return authorizeData , true
162+ }
163+
107164func renderToken (w io.Writer , data tokenData ) {
108165 if err := tokenTemplate .Execute (w , data ); err != nil {
109166 utilruntime .HandleError (fmt .Errorf ("unable to render token template: %v" , err ))
110167 }
111168}
112169
170+ type sharedData struct {
171+ Error string
172+ RequestURL string
173+ }
174+
113175type tokenData struct {
114- Error string
176+ sharedData
177+
115178 AccessToken string
116- RequestURL string
117179 PublicMasterURL string
118180 LogoutURL string
119181}
120182
121- // TODO: allow template to be read from an external file
122- var tokenTemplate = template .Must (template .New ("tokenTemplate" ).Parse (`
183+ func getBaseURL (req * http.Request ) (* url.URL , error ) {
184+ uri , err := url .Parse (req .RequestURI )
185+ if err != nil {
186+ return nil , err
187+ }
188+ uri .Scheme , uri .Host , uri .RawQuery , uri .Fragment = req .URL .Scheme , req .URL .Host , "" , ""
189+ return uri , nil
190+ }
191+
192+ type formData struct {
193+ sharedData
194+
195+ Action string
196+ Code string
197+ CSRF string
198+ }
199+
200+ func renderForm (w io.Writer , data formData ) {
201+ if err := formTemplate .Execute (w , data ); err != nil {
202+ utilruntime .HandleError (fmt .Errorf ("unable to render form template: %v" , err ))
203+ }
204+ }
205+
206+ const cssStyle = `
123207<style>
124208 body { font-family: sans-serif; font-size: 14px; margin: 2em 2%; background-color: #F9F9F9; }
125209 h2 { font-size: 1.4em;}
@@ -135,7 +219,10 @@ var tokenTemplate = template.Must(template.New("tokenTemplate").Parse(`
135219 .nowrap { white-space: nowrap; }
136220 }
137221</style>
222+ `
138223
224+ var tokenTemplate = template .Must (template .New ("tokenTemplate" ).Parse (
225+ cssStyle + `
139226{{ if .Error }}
140227 {{ .Error }}
141228{{ else }}
@@ -163,6 +250,23 @@ var tokenTemplate = template.Must(template.New("tokenTemplate").Parse(`
163250{{ end }}
164251` ))
165252
253+ var formTemplate = template .Must (template .New ("formTemplate" ).Parse (
254+ cssStyle + `
255+ {{ if .Error }}
256+ {{ .Error }}
257+ <br><br>
258+ <a href="{{.RequestURL}}">Request another token</a>
259+ {{ else }}
260+ <form method="post" action="{{.Action}}">
261+ <input type="hidden" name="code" value="{{.Code}}">
262+ <input type="hidden" name="csrf" value="{{.CSRF}}">
263+ <button type="submit">
264+ Display Token
265+ </button>
266+ </form>
267+ {{ end }}
268+ ` ))
269+
166270func (t * tokenRequest ) implicitToken (w http.ResponseWriter , req * http.Request ) {
167271 w .Header ().Set ("Content-Type" , "text/plain" )
168272 _ , _ = w .Write ([]byte (`
0 commit comments