Skip to content

Commit ebaed13

Browse files
Fix invalid tokens
1 parent a17db7b commit ebaed13

35 files changed

+498
-346
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ cf-cli-plugin
44
mta-op-*/
55
.DS_Store
66
*/.DS_Store
7+
.idea
78
mta_plugin_darwin_amd64
89
mta_plugin_linux_amd64
910
mta_plugin_windows_amd64.exe

clients/baseclient/client_util.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ func shouldRetry(err error) bool {
2323
if err == nil {
2424
return false
2525
}
26-
isMatching, _ := regexp.MatchString(" EOF$", err.Error())
27-
if isMatching {
26+
if isMatching(err) {
2827
return true
2928
}
3029
ae, ok := err.(*ClientError)
@@ -38,6 +37,16 @@ func shouldRetry(err error) bool {
3837
return false
3938
}
4039

40+
func isMatching(err error) bool {
41+
return strings.Contains(err.Error(), "retry is needed") || isErrorEOF(err)
42+
}
43+
44+
func isErrorEOF(err error) bool {
45+
isMatching, _ := regexp.MatchString(" EOF$", err.Error())
46+
47+
return isMatching
48+
}
49+
4150
func EncodeArg(arg string) string {
4251
return strings.Replace(url.QueryEscape(arg), "+", "%20", -1)
4352
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package csrf_paramters
2+
3+
type CsrfRequestHeader struct {
4+
CsrfTokenHeader string
5+
CsrfTokenValue string
6+
}

clients/csrf/csrf_suite_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package csrf_test
2+
3+
import (
4+
. "github.com/onsi/ginkgo"
5+
. "github.com/onsi/gomega"
6+
7+
"testing"
8+
)
9+
10+
func TestRestclient(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Csrf suite")
13+
}

clients/csrf/csrf_token_fetcher.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package csrf
2+
3+
import (
4+
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/csrf/csrf_paramters"
5+
"net/http"
6+
)
7+
8+
type CsrfTokenFetcher interface {
9+
FetchCsrfToken(url string, currentRequest *http.Request) (*csrf_paramters.CsrfRequestHeader, error)
10+
}

clients/csrf/csrf_token_updater.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package csrf
2+
3+
import "net/http"
4+
5+
type CsrfTokenUpdater interface {
6+
checkAndUpdateCsrfToken() error
7+
initializeToken(forceInitializing bool, url string) error
8+
isRetryNeeded(response *http.Response) (bool, error)
9+
updateCurrentCsrfToken(request *http.Request, t *Transport)
10+
isProtectionRequired(req *http.Request, t *Transport) bool
11+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package csrf
2+
3+
import (
4+
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/csrf/fakes"
5+
. "github.com/onsi/ginkgo"
6+
. "github.com/onsi/gomega"
7+
"net/http"
8+
"net/url"
9+
)
10+
11+
const testUrl = "http://localhost:1000"
12+
13+
const csrfTokenNotSet = ""
14+
15+
var _ = Describe("DefaultCsrfTokenUpdater", func() {
16+
Context("", func() {
17+
It("protection not needed", func() {
18+
transport, request := createTransport(), createRequest(http.MethodGet)
19+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
20+
Expect(csrfTokenManager.isProtectionRequired(request, transport)).To(BeFalse())
21+
})
22+
It("protection not needed", func() {
23+
transport, request := createTransport(), createRequest(http.MethodOptions)
24+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
25+
Expect(csrfTokenManager.isProtectionRequired(request, transport)).To(BeFalse())
26+
})
27+
It("protection not needed", func() {
28+
transport, request := createTransport(), createRequest(http.MethodHead)
29+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
30+
Expect(csrfTokenManager.isProtectionRequired(request, transport)).To(BeFalse())
31+
})
32+
It("protection needed", func() {
33+
transport, request := createTransport(), createRequest(http.MethodPost)
34+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
35+
Expect(csrfTokenManager.isProtectionRequired(request, transport)).To(BeTrue())
36+
})
37+
It("retry is not needed", func() {
38+
transport, request := createTransport(), createRequest(http.MethodPost)
39+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
40+
Expect(csrfTokenManager.isRetryNeeded(createResponse(http.StatusOK, ""))).To(BeFalse())
41+
})
42+
It("retry is not needed", func() {
43+
transport, request := createTransport(), createRequest(http.MethodPost)
44+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, NewDefaultCsrfTokenFetcher(transport))
45+
Expect(csrfTokenManager.isRetryNeeded(createResponse(http.StatusForbidden, CsrfTokenHeaderRequiredValue))).To(BeFalse())
46+
})
47+
It("retry is needed", func() {
48+
transport := createTransport()
49+
transport.Csrf.IsInitialized = true
50+
request := createRequest(http.MethodPost)
51+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
52+
isRetryNeeded, err := csrfTokenManager.isRetryNeeded(createResponse(http.StatusForbidden, CsrfTokenHeaderRequiredValue))
53+
Ω(err).ShouldNot(HaveOccurred())
54+
Expect(isRetryNeeded).To(BeTrue())
55+
})
56+
It("initialize new token", func() {
57+
transport := createTransport()
58+
transport.Csrf.IsInitialized = true
59+
request := createRequest(http.MethodPost)
60+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
61+
err := csrfTokenManager.initializeToken(true, testUrl)
62+
Ω(err).ShouldNot(HaveOccurred())
63+
Expect(transport.Csrf.Header).To(Equal(fakes.FakeCsrfTokenHeader))
64+
Expect(transport.Csrf.Token).To(Equal(fakes.FakeCsrfTokenValue))
65+
Expect(transport.Csrf.IsInitialized).To(BeTrue())
66+
})
67+
It("update current csrf tokens", func() {
68+
transport := createTransport()
69+
request := createRequest(http.MethodGet)
70+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
71+
err := csrfTokenManager.initializeToken(true, testUrl)
72+
Ω(err).ShouldNot(HaveOccurred())
73+
csrfTokenManager.updateCurrentCsrfToken(request, transport)
74+
expectCsrfTokenIsProperlySet(request, fakes.FakeCsrfTokenHeader, fakes.FakeCsrfTokenValue)
75+
})
76+
It("should not update csrf tokens", func() {
77+
transport, request := createTransport(), createRequest(http.MethodGet)
78+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
79+
err := csrfTokenManager.updateCsrfToken()
80+
Ω(err).ShouldNot(HaveOccurred())
81+
expectCsrfTokenIsProperlySet(request, csrfTokenNotSet, csrfTokenNotSet)
82+
})
83+
It("should not update csrf tokens", func() {
84+
transport, request := createTransport(), createRequest(http.MethodPost)
85+
transport.Csrf.IsInitialized = true
86+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
87+
err := csrfTokenManager.updateCsrfToken()
88+
Ω(err).ShouldNot(HaveOccurred())
89+
expectCsrfTokenIsProperlySet(request, csrfTokenNotSet, csrfTokenNotSet)
90+
})
91+
It("should not update csrf tokens", func() {
92+
transport, request := createTransport(), createRequest(http.MethodGet)
93+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
94+
err := csrfTokenManager.updateCsrfToken()
95+
Ω(err).ShouldNot(HaveOccurred())
96+
expectCsrfTokenIsProperlySet(request, csrfTokenNotSet, csrfTokenNotSet)
97+
})
98+
It("should update csrf tokens", func() {
99+
transport, request := createTransport(), createRequest(http.MethodPost)
100+
csrfTokenManager := NewDefaultCsrfTokenUpdater(transport, request, fakes.NewFakeCsrfTokenFetcher())
101+
err := csrfTokenManager.updateCsrfToken()
102+
Ω(err).ShouldNot(HaveOccurred())
103+
expectCsrfTokenIsProperlySet(request, fakes.FakeCsrfTokenHeader, fakes.FakeCsrfTokenValue)
104+
})
105+
Context("set cookies in the request, valid cookies", func() {
106+
It("should be equal", func() {
107+
request := createRequest(http.MethodGet)
108+
cookies := createValidCookies()
109+
UpdateCookiesIfNeeded(cookies, request)
110+
Expect(cookies).To(Equal(request.Cookies()))
111+
})
112+
})
113+
})
114+
})
115+
116+
func expectCsrfTokenIsProperlySet(request *http.Request, csrfTokenHeader, csrfTokenValue string) {
117+
Expect(request.Header.Get(XCsrfHeader)).To(Equal(csrfTokenHeader))
118+
Expect(request.Header.Get(XCsrfToken)).To(Equal(csrfTokenValue))
119+
}
120+
121+
func createResponse(httpStatusCode int, csrfToken string) *http.Response {
122+
response := &http.Response{}
123+
response.Header = make(http.Header)
124+
response.StatusCode = httpStatusCode
125+
response.Header.Set(XCsrfToken, csrfToken)
126+
127+
return response
128+
}
129+
130+
func createTransport() *Transport {
131+
return &Transport{http.DefaultTransport.(*http.Transport),
132+
&Csrf{"", "", false, getNonProtectedMethods()}, &Cookies{[]*http.Cookie{}}}
133+
}
134+
135+
func getNonProtectedMethods() map[string]bool {
136+
nonProtectedMethods := make(map[string]bool)
137+
138+
nonProtectedMethods[http.MethodGet] = true
139+
nonProtectedMethods[http.MethodHead] = true
140+
nonProtectedMethods[http.MethodOptions] = true
141+
142+
return nonProtectedMethods
143+
}
144+
145+
func createValidCookies() []*http.Cookie {
146+
var cookies []*http.Cookie
147+
cookie1 := &http.Cookie{}
148+
cookie1.Name = "JSESSION"
149+
cookie1.Value = "123"
150+
cookie2 := &http.Cookie{}
151+
cookie2.Name = "__V_CAP__"
152+
cookie2.Value = "321"
153+
cookies = append(cookies, cookie1)
154+
cookies = append(cookies, cookie2)
155+
156+
return cookies
157+
}
158+
159+
func createRequest(method string) *http.Request {
160+
request := &http.Request{}
161+
requestUrl := &url.URL{}
162+
requestUrl.Scheme = "http"
163+
requestUrl.Host = "localhost:1000"
164+
request.URL = requestUrl
165+
request.Header = make(http.Header)
166+
request.Method = method
167+
168+
return request
169+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package csrf
2+
3+
import (
4+
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/csrf/csrf_paramters"
5+
"github.com/cloudfoundry/cli/plugin"
6+
"net/http"
7+
"os"
8+
)
9+
10+
const CsrfTokenHeaderFetchValue = "Fetch"
11+
const CsrfTokensApi = "/api/v1/csrf-token"
12+
const ContentTypeHeader = "Content-Type"
13+
const AuthorizationHeader = "Authorization"
14+
const ApplicationJsonContentType = "application/json"
15+
const CookieHeader = "CookieHeader"
16+
17+
type DefaultCsrfTokenFetcher struct {
18+
transport *Transport
19+
}
20+
21+
func NewDefaultCsrfTokenFetcher(transport *Transport) *DefaultCsrfTokenFetcher {
22+
return &DefaultCsrfTokenFetcher{transport: transport}
23+
}
24+
25+
func (c *DefaultCsrfTokenFetcher) FetchCsrfToken(url string, currentRequest *http.Request) (*csrf_paramters.CsrfRequestHeader, error) {
26+
fetchTokenRequest, err := http.NewRequest(http.MethodGet, url, nil)
27+
if err != nil {
28+
return nil, err
29+
}
30+
fetchTokenRequest.Header.Set(XCsrfToken, CsrfTokenHeaderFetchValue)
31+
fetchTokenRequest.Header.Set(ContentTypeHeader, ApplicationJsonContentType)
32+
33+
cliConnection := plugin.NewCliConnection(os.Args[1])
34+
token, err := cliConnection.AccessToken()
35+
if err != nil {
36+
return nil, err
37+
}
38+
fetchTokenRequest.Header.Set(AuthorizationHeader, token)
39+
UpdateCookiesIfNeeded(currentRequest.Cookies(), fetchTokenRequest)
40+
41+
response, err := c.transport.Transport.RoundTrip(fetchTokenRequest)
42+
if err != nil {
43+
return nil, err
44+
}
45+
if len(response.Cookies()) != 0 {
46+
fetchTokenRequest.Header.Del(CookieHeader)
47+
UpdateCookiesIfNeeded(response.Cookies(), fetchTokenRequest)
48+
49+
c.transport.Cookies.Cookies = fetchTokenRequest.Cookies()
50+
51+
response, err = c.transport.Transport.RoundTrip(fetchTokenRequest)
52+
53+
if err != nil {
54+
return nil, err
55+
}
56+
}
57+
58+
return &csrf_paramters.CsrfRequestHeader{response.Header.Get(XCsrfHeader), response.Header.Get(XCsrfToken)}, nil
59+
}
60+
61+
func getCsrfTokenUrl(req *http.Request) string {
62+
return string(req.URL.Scheme) + "://" + string(req.URL.Host) + CsrfTokensApi
63+
}
64+
65+
func UpdateCookiesIfNeeded(cookies []*http.Cookie, request *http.Request) {
66+
if cookies != nil {
67+
request.Header.Del(CookieHeader)
68+
for _, cookie := range cookies {
69+
request.AddCookie(cookie)
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)