@@ -2,6 +2,9 @@ package akamai
22
33import (
44 "bytes"
5+ "crypto/hmac"
6+ "crypto/sha256"
7+ "encoding/base64"
58 "encoding/json"
69 "fmt"
710 "io/ioutil"
@@ -18,6 +21,7 @@ import (
1821)
1922
2023func TestConstructAuthHeader (t * testing.T ) {
24+ log := blog .NewMock ()
2125 stats := metrics .NewNoopScope ()
2226 cpc , err := NewCachePurgeClient (
2327 "https://akaa-baseurl-xxxxxxxxxxx-xxxxxxxxxxxxx.luna.akamaiapis.net" ,
@@ -27,15 +31,15 @@ func TestConstructAuthHeader(t *testing.T) {
2731 "production" ,
2832 0 ,
2933 time .Second ,
30- nil ,
34+ log ,
3135 stats ,
3236 )
3337 test .AssertNotError (t , err , "Failed to create cache purge client" )
3438 fc := clock .NewFake ()
3539 cpc .clk = fc
3640 wantedTimestamp , err := time .Parse (timestampFormat , "20140321T19:34:21+0000" )
3741 test .AssertNotError (t , err , "Failed to parse timestamp" )
38- fc .Add (wantedTimestamp . Sub ( fc . Now ()) )
42+ fc .Set (wantedTimestamp )
3943
4044 req , err := http .NewRequest (
4145 "POST" ,
@@ -58,6 +62,7 @@ func TestConstructAuthHeader(t *testing.T) {
5862type akamaiServer struct {
5963 responseCode int
6064 v3 bool
65+ * httptest.Server
6166}
6267
6368func (as * akamaiServer ) sendResponse (w http.ResponseWriter , resp purgeResponse ) {
@@ -95,6 +100,13 @@ func (as *akamaiServer) akamaiHandler(w http.ResponseWriter, r *http.Request) {
95100 return
96101 }
97102
103+ err = as .checkSignature (r , body )
104+ if err != nil {
105+ fmt .Printf ("Error checking signature: %s\n " , err )
106+ w .WriteHeader (http .StatusInternalServerError )
107+ return
108+ }
109+
98110 // Enforce that a V3 request is well formed and does not include the "Type"
99111 // and "Action" fields used by the V2 api.
100112 if as .v3 == true && (req .Type != "" || req .Action != "" ) {
@@ -129,19 +141,58 @@ func (as *akamaiServer) akamaiHandler(w http.ResponseWriter, r *http.Request) {
129141 as .sendResponse (w , resp )
130142}
131143
144+ func (as * akamaiServer ) checkSignature (r * http.Request , body []byte ) error {
145+ bodyHash := sha256 .Sum256 (body )
146+ bodyHashB64 := base64 .StdEncoding .EncodeToString (bodyHash [:])
147+
148+ authorization := r .Header .Get ("Authorization" )
149+ authValues := make (map [string ]string )
150+ for _ , v := range strings .Split (authorization , ";" ) {
151+ splitValue := strings .Split (v , "=" )
152+ authValues [splitValue [0 ]] = splitValue [1 ]
153+ }
154+ headerTimestamp := authValues ["timestamp" ]
155+ splitHeader := strings .Split (authorization , "signature=" )
156+ shortenedHeader , signature := splitHeader [0 ], splitHeader [1 ]
157+ hostPort := strings .Split (as .URL , "://" )[1 ]
158+ // Assume all unittests use "secret" as the client secret.
159+ h := hmac .New (sha256 .New , signingKey ("secret" , headerTimestamp ))
160+ input := []byte (fmt .Sprintf ("POST\t http\t %s\t %s\t \t %s\t %s" ,
161+ hostPort ,
162+ r .URL .Path ,
163+ bodyHashB64 ,
164+ shortenedHeader ,
165+ ))
166+ h .Write (input )
167+ expectedSignature := base64 .StdEncoding .EncodeToString (h .Sum (nil ))
168+ if signature != expectedSignature {
169+ return fmt .Errorf ("Wrong signature %q in %q. Expected %q\n " ,
170+ signature , authorization , expectedSignature )
171+ }
172+ return nil
173+ }
174+
175+ func newAkamaiServer (code int , v3 bool ) * akamaiServer {
176+ m := http .NewServeMux ()
177+ as := akamaiServer {
178+ responseCode : code ,
179+ v3 : v3 ,
180+ Server : httptest .NewServer (m ),
181+ }
182+ m .HandleFunc ("/" , as .akamaiHandler )
183+ return & as
184+ }
185+
132186// TestV2Purge tests the legacy CCU v2 Akamai API used when the v3Network
133187// parameter to NewCachePurgeClient is "".
134188func TestV2Purge (t * testing.T ) {
135189 log := blog .NewMock ()
136190
137- as := akamaiServer {responseCode : http .StatusCreated }
138- m := http .NewServeMux ()
139- server := httptest .NewUnstartedServer (m )
140- m .HandleFunc ("/" , as .akamaiHandler )
141- server .Start ()
191+ as := newAkamaiServer (http .StatusCreated , false )
192+ defer as .Close ()
142193
143194 client , err := NewCachePurgeClient (
144- server .URL ,
195+ as .URL ,
145196 "token" ,
146197 "secret" ,
147198 "accessToken" ,
@@ -174,31 +225,24 @@ func TestV2Purge(t *testing.T) {
174225// TestV3Purge tests the Akamai CCU v3 purge API by setting the v3Network
175226// parameter to "production".
176227func TestV3Purge (t * testing.T ) {
177- log := blog .NewMock ()
178-
179- as := akamaiServer {
180- responseCode : http .StatusCreated ,
181- v3 : true ,
182- }
183- m := http .NewServeMux ()
184- server := httptest .NewUnstartedServer (m )
185- m .HandleFunc ("/" , as .akamaiHandler )
186- server .Start ()
228+ as := newAkamaiServer (http .StatusCreated , true )
229+ defer as .Close ()
187230
188231 // Client is a purge client with a "production" v3Network parameter
189232 client , err := NewCachePurgeClient (
190- server .URL ,
233+ as .URL ,
191234 "token" ,
192235 "secret" ,
193236 "accessToken" ,
194237 "production" ,
195238 3 ,
196239 time .Second ,
197- log ,
240+ blog . NewMock () ,
198241 metrics .NewNoopScope (),
199242 )
200243 test .AssertNotError (t , err , "Failed to create CachePurgeClient" )
201- client .clk = clock .NewFake ()
244+ fc := clock .NewFake ()
245+ client .clk = fc
202246
203247 err = client .Purge ([]string {"http://test.com" })
204248 test .AssertNotError (t , err , "Purge failed with 201 response" )
@@ -217,34 +261,30 @@ func TestV3Purge(t *testing.T) {
217261}
218262
219263func TestNewCachePurgeClient (t * testing.T ) {
220- log := blog .NewMock ()
221- m := http .NewServeMux ()
222- server := httptest .NewUnstartedServer (m )
223-
224264 // Creating a new cache purge client with an invalid "network" parameter should error
225265 _ , err := NewCachePurgeClient (
226- server . URL ,
266+ "http://127.0.0.1:9000/" ,
227267 "token" ,
228268 "secret" ,
229269 "accessToken" ,
230270 "fake" ,
231271 3 ,
232272 time .Second ,
233- log ,
273+ blog . NewMock () ,
234274 metrics .NewNoopScope (),
235275 )
236276 test .AssertError (t , err , "NewCachePurgeClient with invalid network parameter didn't error" )
237277
238278 // Creating a new cache purge client with a valid "network" parameter shouldn't error
239279 _ , err = NewCachePurgeClient (
240- server . URL ,
280+ "http://127.0.0.1:9000/" ,
241281 "token" ,
242282 "secret" ,
243283 "accessToken" ,
244284 "staging" ,
245285 3 ,
246286 time .Second ,
247- log ,
287+ blog . NewMock () ,
248288 metrics .NewNoopScope (),
249289 )
250290 test .AssertNotError (t , err , "NewCachePurgeClient with valid network parameter errored" )
@@ -258,7 +298,7 @@ func TestNewCachePurgeClient(t *testing.T) {
258298 "staging" ,
259299 3 ,
260300 time .Second ,
261- log ,
301+ blog . NewMock () ,
262302 metrics .NewNoopScope (),
263303 )
264304 test .AssertError (t , err , "NewCachePurgeClient with invalid server url parameter didn't error" )
@@ -267,17 +307,17 @@ func TestNewCachePurgeClient(t *testing.T) {
267307func TestBigBatchPurge (t * testing.T ) {
268308 log := blog .NewMock ()
269309
310+ m := http .NewServeMux ()
270311 as := akamaiServer {
271312 responseCode : http .StatusCreated ,
272313 v3 : true ,
314+ Server : httptest .NewUnstartedServer (m ),
273315 }
274- m := http .NewServeMux ()
275- server := httptest .NewUnstartedServer (m )
276316 m .HandleFunc ("/" , as .akamaiHandler )
277- server .Start ()
317+ as .Start ()
278318
279319 client , err := NewCachePurgeClient (
280- server .URL ,
320+ as .URL ,
281321 "token" ,
282322 "secret" ,
283323 "accessToken" ,
0 commit comments