Merge lp:~vds/usso/handle_order_of_parameters into lp:usso
- handle_order_of_parameters
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Vincenzo Di Somma |
Approved revision: | 25 |
Merged at revision: | 25 |
Proposed branch: | lp:~vds/usso/handle_order_of_parameters |
Merge into: | lp:usso |
Diff against target: |
506 lines (+191/-141) 6 files modified
example/usso_example.go (+7/-12) oauth.go (+90/-71) oauth_test.go (+41/-26) url.go (+36/-19) url_test.go (+2/-2) usso.go (+15/-11) |
To merge this branch: | bzr merge lp:~vds/usso/handle_order_of_parameters |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | code | Approve | |
Domas Monkus (community) | Approve | ||
Review via email: mp+145936@code.launchpad.net |
Commit message
"Got rid of useless error, using a struct to handle the oauth_signature
Description of the change
"Got rid of useless error, using a struct to handle the oauth_signature
Graham Binns (gmb) wrote : | # |
Hi Vincenzo,
Thank you for this branch! There are a couple of things you need to add before you land this, but there's nothing that needs me to re-review it after you've made the changes, so feel free to make them and then mark the MP as approved.
[1]
149 +func (PLAINTEXT) Name() string { return "PLAINTEXT" }
150 +func (PLAINTEXT) Signature(
160 +func (HMACSHA1) Name() string { return "HMAC-SHA1" }
161 +func (HMACSHA1) Signature(
These all need some documentation comments.
[2]
275 +// Test the request signing with oauth_signature
301 // Test the request signing with oauth_signature
We should avoid the "Test the..." style when writing documentation for tests. Instead, we should write things like:
// It is possible to sign a request with a PLAINTEXT oauth_signature
// It is possible to sign a request with a SHA1 oauth_signature
Put another way, the documentation on a test should be something that can be declared true if the test passes, false otherwise.
- 25. By Vincenzo Di Somma
-
Fixing/adding comments and cleaning up.
Preview Diff
1 | === modified file 'example/usso_example.go' | |||
2 | --- example/usso_example.go 2013-01-25 16:39:11 +0000 | |||
3 | +++ example/usso_example.go 2013-02-05 12:38:20 +0000 | |||
4 | @@ -9,7 +9,7 @@ | |||
5 | 9 | "net/http" | 9 | "net/http" |
6 | 10 | ) | 10 | ) |
7 | 11 | 11 | ||
9 | 12 | var email, password, tokenName, signature_method string | 12 | var email, password string |
10 | 13 | 13 | ||
11 | 14 | func inputParams() { | 14 | func inputParams() { |
12 | 15 | fmt.Println("This application will query the staging Ubuntu SSO Server" + | 15 | fmt.Println("This application will query the staging Ubuntu SSO Server" + |
13 | @@ -19,9 +19,6 @@ | |||
14 | 19 | fmt.Print("Enter password: ") | 19 | fmt.Print("Enter password: ") |
15 | 20 | fmt.Scanf("%s", &password) | 20 | fmt.Scanf("%s", &password) |
16 | 21 | fmt.Print("Enter token name: ") | 21 | fmt.Print("Enter token name: ") |
17 | 22 | fmt.Scanf("%s", &tokenName) | ||
18 | 23 | fmt.Print("Enter signature method (PLAINTEXT or HMAC-SHA1): ") | ||
19 | 24 | fmt.Scanf("%s", &signature_method) | ||
20 | 25 | } | 22 | } |
21 | 26 | 23 | ||
22 | 27 | func main() { | 24 | func main() { |
23 | @@ -32,7 +29,7 @@ | |||
24 | 32 | server := usso.StagingUbuntuSSOServer | 29 | server := usso.StagingUbuntuSSOServer |
25 | 33 | // One would use server := usso.ProductionUbuntuSSOServer | 30 | // One would use server := usso.ProductionUbuntuSSOServer |
26 | 34 | // to use the production Ubuntu SSO Server. | 31 | // to use the production Ubuntu SSO Server. |
28 | 35 | ssodata, err := server.GetToken(email, password, tokenName) | 32 | ssodata, err := server.GetToken(email, password, "usso") |
29 | 36 | if err != nil { | 33 | if err != nil { |
30 | 37 | panic(err) | 34 | panic(err) |
31 | 38 | } | 35 | } |
32 | @@ -48,14 +45,12 @@ | |||
33 | 48 | //fmt.Printf("Got accounts info: %s\n", accounts) | 45 | //fmt.Printf("Got accounts info: %s\n", accounts) |
34 | 49 | 46 | ||
35 | 50 | // But this shows how to sign a generic request. | 47 | // But this shows how to sign a generic request. |
37 | 51 | ssodata.BaseURL = fmt.Sprintf( | 48 | rp := usso.RequestParameters{BaseURL: fmt.Sprintf( |
38 | 52 | "https://login.staging.ubuntu.com/api/v2/accounts/%s", | 49 | "https://login.staging.ubuntu.com/api/v2/accounts/%s", |
45 | 53 | ssodata.ConsumerKey) | 50 | ssodata.ConsumerKey), HTTPMethod: "GET", |
46 | 54 | ssodata.HTTPMethod = "GET" | 51 | SignatureMethod: usso.HMACSHA1{}} |
47 | 55 | ssodata.SignatureMethod = signature_method | 52 | request, _ := http.NewRequest(rp.HTTPMethod, rp.BaseURL, nil) |
48 | 56 | request, _ := http.NewRequest(ssodata.HTTPMethod, ssodata.BaseURL, nil) | 53 | usso.SignRequest(ssodata, &rp, request) |
43 | 57 | usso.SignRequest(ssodata, request) | ||
44 | 58 | |||
49 | 59 | if err != nil { | 54 | if err != nil { |
50 | 60 | fmt.Printf("Error: %s\n", err) | 55 | fmt.Printf("Error: %s\n", err) |
51 | 61 | } | 56 | } |
52 | 62 | 57 | ||
53 | === modified file 'oauth.go' | |||
54 | --- oauth.go 2013-01-29 11:23:37 +0000 | |||
55 | +++ oauth.go 2013-02-05 12:38:20 +0000 | |||
56 | @@ -4,7 +4,6 @@ | |||
57 | 4 | "crypto/hmac" | 4 | "crypto/hmac" |
58 | 5 | "crypto/sha1" | 5 | "crypto/sha1" |
59 | 6 | "encoding/base64" | 6 | "encoding/base64" |
60 | 7 | "errors" | ||
61 | 8 | "fmt" | 7 | "fmt" |
62 | 9 | "math/rand" | 8 | "math/rand" |
63 | 10 | "net/http" | 9 | "net/http" |
64 | @@ -30,72 +29,91 @@ | |||
65 | 30 | 29 | ||
66 | 31 | // Contains the oauth data to perform a request. | 30 | // Contains the oauth data to perform a request. |
67 | 32 | type SSOData struct { | 31 | type SSOData struct { |
123 | 33 | HTTPMethod string `json:"-"` | 32 | ConsumerKey string `json:"consumer_key"` |
124 | 34 | BaseURL string `json:"-"` | 33 | ConsumerSecret string `json:"consumer_secret"` |
125 | 35 | Params url.Values `json:"-"` | 34 | TokenKey string `json:"token_key"` |
126 | 36 | Nonce string `json:"-"` | 35 | TokenName string `json:"token_name"` |
127 | 37 | Timestamp string `json:"-"` | 36 | TokenSecret string `json:"token_secret"` |
128 | 38 | SignatureMethod string `json:"-"` | 37 | } |
129 | 39 | ConsumerKey string `json:"consumer_key"` | 38 | |
130 | 40 | ConsumerSecret string `json:"consumer_secret"` | 39 | type RequestParameters struct { |
131 | 41 | TokenKey string `json:"token_key"` | 40 | HTTPMethod string |
132 | 42 | TokenName string `json:"token_name"` | 41 | BaseURL string |
133 | 43 | TokenSecret string `json:"token_secret"` | 42 | Params url.Values |
134 | 44 | } | 43 | Nonce string |
135 | 45 | 44 | Timestamp string | |
136 | 46 | // Depending on the signature method, create the signature from the | 45 | SignatureMethod SignatureMethod |
137 | 47 | // consumer secret, the token secret and, if required, the URL. | 46 | } |
138 | 48 | // Supported signature methods are PLAINTEXT and HMAC-SHA1. | 47 | |
139 | 49 | func (oauth *SSOData) signature() (string, error) { | 48 | type SignatureMethod interface { |
140 | 50 | switch oauth.SignatureMethod { | 49 | Name() string |
141 | 51 | case "PLAINTEXT": | 50 | Signature( |
142 | 52 | return fmt.Sprintf( | 51 | ssodata *SSOData, rp *RequestParameters) (string, error) |
143 | 53 | `%s%%26%s`, | 52 | } |
144 | 54 | oauth.ConsumerSecret, | 53 | |
145 | 55 | oauth.TokenSecret), nil | 54 | type PLAINTEXT struct{} |
146 | 56 | case "HMAC-SHA1": | 55 | |
147 | 57 | base_url, err := NormalizeURL(oauth.BaseURL) | 56 | // Return the name of the signature method, used to compose the |
148 | 58 | if err != nil { | 57 | // Authentication Header. |
149 | 59 | return "", err | 58 | func (PLAINTEXT) Name() string { return "PLAINTEXT" } |
150 | 60 | } | 59 | |
151 | 61 | params, err := NormalizeParameters(oauth.Params) | 60 | // Calculate the oaut_signature part of the Authentication Header. |
152 | 62 | if err != nil { | 61 | func (PLAINTEXT) Signature( |
153 | 63 | return "", err | 62 | ssodata *SSOData, rp *RequestParameters) (string, error) { |
154 | 64 | } | 63 | return fmt.Sprintf( |
155 | 65 | base_string := fmt.Sprintf(`%s&%s&%s%s%s%s%s%s%s`, | 64 | `%s&%s`, |
156 | 66 | oauth.HTTPMethod, | 65 | ssodata.ConsumerSecret, |
157 | 67 | url.QueryEscape(base_url), | 66 | ssodata.TokenSecret), nil |
158 | 68 | url.QueryEscape(params), | 67 | } |
159 | 69 | url.QueryEscape("oauth_consumer_key="+oauth.ConsumerKey), | 68 | |
160 | 70 | url.QueryEscape("&oauth_nonce="+oauth.Nonce), | 69 | type HMACSHA1 struct{} |
161 | 71 | url.QueryEscape("&oauth_signature_method="+oauth.SignatureMethod), | 70 | |
162 | 72 | url.QueryEscape("&oauth_timestamp="+oauth.Timestamp), | 71 | // Return the name of the signature method, used to compose the |
163 | 73 | url.QueryEscape("&oauth_token="+oauth.TokenKey), | 72 | // Authentication Header. |
164 | 74 | url.QueryEscape("&oauth_version=1.0")) | 73 | func (HMACSHA1) Name() string { return "HMAC-SHA1" } |
165 | 75 | hashfun := hmac.New(sha1.New, []byte( | 74 | |
166 | 76 | oauth.ConsumerSecret+"&"+oauth.TokenSecret)) | 75 | // Calculate the oaut_signature part of the Authentication Header. |
167 | 77 | hashfun.Write([]byte(base_string)) | 76 | func (HMACSHA1) Signature( |
168 | 78 | rawsignature := hashfun.Sum(nil) | 77 | ssodata *SSOData, rp *RequestParameters) (string, error) { |
169 | 79 | base64signature := make( | 78 | baseUrl, err := NormalizeURL(rp.BaseURL) |
170 | 80 | []byte, base64.StdEncoding.EncodedLen(len(rawsignature))) | 79 | if err != nil { |
171 | 81 | base64.StdEncoding.Encode(base64signature, rawsignature) | 80 | return "", err |
172 | 82 | return string(base64signature), nil | 81 | } |
173 | 83 | default: | 82 | params, err := NormalizeParameters(rp.Params) |
174 | 84 | return "", errors.New( | 83 | if err != nil { |
175 | 85 | "usso/oauth: Oauth Signature Method not supported.") | 84 | return "", err |
176 | 86 | } | 85 | } |
177 | 87 | return "", nil | 86 | baseString := fmt.Sprintf(`%s&%s&%s%s%s%s%s%s%s`, |
178 | 87 | rp.HTTPMethod, | ||
179 | 88 | url.QueryEscape(baseUrl), | ||
180 | 89 | url.QueryEscape(params), | ||
181 | 90 | url.QueryEscape("oauth_consumer_key="+ssodata.ConsumerKey), | ||
182 | 91 | url.QueryEscape("&oauth_nonce="+rp.Nonce), | ||
183 | 92 | url.QueryEscape( | ||
184 | 93 | "&oauth_signature_method="+string(rp.SignatureMethod.Name())), | ||
185 | 94 | url.QueryEscape("&oauth_timestamp="+rp.Timestamp), | ||
186 | 95 | url.QueryEscape("&oauth_token="+ssodata.TokenKey), | ||
187 | 96 | url.QueryEscape("&oauth_version=1.0")) | ||
188 | 97 | hashfun := hmac.New(sha1.New, []byte( | ||
189 | 98 | ssodata.ConsumerSecret+"&"+ssodata.TokenSecret)) | ||
190 | 99 | hashfun.Write([]byte(baseString)) | ||
191 | 100 | rawsignature := hashfun.Sum(nil) | ||
192 | 101 | base64signature := make( | ||
193 | 102 | []byte, base64.StdEncoding.EncodedLen(len(rawsignature))) | ||
194 | 103 | base64.StdEncoding.Encode(base64signature, rawsignature) | ||
195 | 104 | return string(base64signature), nil | ||
196 | 88 | } | 105 | } |
197 | 89 | 106 | ||
198 | 90 | // Sign the provided request. | 107 | // Sign the provided request. |
207 | 91 | func (oauth *SSOData) GetAuthorizationHeader() (string, error) { | 108 | func (ssodata *SSOData) GetAuthorizationHeader( |
208 | 92 | if oauth.Nonce == "" { | 109 | rp *RequestParameters) (string, error) { |
209 | 93 | oauth.Nonce = nonce() | 110 | if rp.Nonce == "" { |
210 | 94 | } | 111 | rp.Nonce = nonce() |
211 | 95 | if oauth.Timestamp == "" { | 112 | } |
212 | 96 | oauth.Timestamp = timestamp() | 113 | if rp.Timestamp == "" { |
213 | 97 | } | 114 | rp.Timestamp = timestamp() |
214 | 98 | signature, err := oauth.signature() | 115 | } |
215 | 116 | signature, err := rp.SignatureMethod.Signature(ssodata, rp) | ||
216 | 99 | if err != nil { | 117 | if err != nil { |
217 | 100 | return "", err | 118 | return "", err |
218 | 101 | } | 119 | } |
219 | @@ -108,19 +126,20 @@ | |||
220 | 108 | `oauth_timestamp="%s", `+ | 126 | `oauth_timestamp="%s", `+ |
221 | 109 | `oauth_nonce="%s", `+ | 127 | `oauth_nonce="%s", `+ |
222 | 110 | `oauth_version="1.0"`, | 128 | `oauth_version="1.0"`, |
226 | 111 | url.QueryEscape(oauth.ConsumerKey), | 129 | url.QueryEscape(ssodata.ConsumerKey), |
227 | 112 | url.QueryEscape(oauth.TokenKey), | 130 | url.QueryEscape(ssodata.TokenKey), |
228 | 113 | oauth.SignatureMethod, | 131 | rp.SignatureMethod.Name(), |
229 | 114 | signature, | 132 | signature, |
232 | 115 | url.QueryEscape(oauth.Timestamp), | 133 | url.QueryEscape(rp.Timestamp), |
233 | 116 | url.QueryEscape(oauth.Nonce)) | 134 | url.QueryEscape(rp.Nonce)) |
234 | 117 | 135 | ||
235 | 118 | return auth, nil | 136 | return auth, nil |
236 | 119 | } | 137 | } |
237 | 120 | 138 | ||
238 | 121 | // Sign the provided request. | 139 | // Sign the provided request. |
241 | 122 | func (oauth *SSOData) SignRequest(req *http.Request) error { | 140 | func (ssodata *SSOData) SignRequest( |
242 | 123 | auth, error := oauth.GetAuthorizationHeader() | 141 | rp *RequestParameters, req *http.Request) error { |
243 | 142 | auth, error := ssodata.GetAuthorizationHeader(rp) | ||
244 | 124 | req.Header.Add("Authorization", auth) | 143 | req.Header.Add("Authorization", auth) |
245 | 125 | return error | 144 | return error |
246 | 126 | } | 145 | } |
247 | 127 | 146 | ||
248 | === modified file 'oauth_test.go' | |||
249 | --- oauth_test.go 2013-01-29 11:23:37 +0000 | |||
250 | +++ oauth_test.go 2013-02-05 12:38:20 +0000 | |||
251 | @@ -6,47 +6,62 @@ | |||
252 | 6 | "net/url" | 6 | "net/url" |
253 | 7 | ) | 7 | ) |
254 | 8 | 8 | ||
256 | 9 | func (suite *USSOTestSuite) TestSignRequestPlainText(c *C) { | 9 | type OAuthTestSuite struct { |
257 | 10 | ssodata SSOData | ||
258 | 11 | rp RequestParameters | ||
259 | 12 | request *http.Request | ||
260 | 13 | } | ||
261 | 14 | |||
262 | 15 | var _ = Suite(&OAuthTestSuite{}) | ||
263 | 16 | |||
264 | 17 | func (suite *OAuthTestSuite) SetUpTest(c *C) { | ||
265 | 10 | baseUrl := "https://localhost" | 18 | baseUrl := "https://localhost" |
267 | 11 | ssodata := SSOData{BaseURL: baseUrl, ConsumerKey: consumerKey, | 19 | suite.ssodata = SSOData{ConsumerKey: consumerKey, |
268 | 12 | ConsumerSecret: consumerSecret, TokenKey: tokenKey, | 20 | ConsumerSecret: consumerSecret, TokenKey: tokenKey, |
269 | 13 | TokenName: tokenName, TokenSecret: tokenSecret} | 21 | TokenName: tokenName, TokenSecret: tokenSecret} |
276 | 14 | request, _ := http.NewRequest("GET", baseUrl, nil) | 22 | suite.rp = RequestParameters{BaseURL: baseUrl, HTTPMethod: "GET", |
277 | 15 | ssodata.HTTPMethod = "GET" | 23 | Nonce: "10888885", Timestamp: "1358853126"} |
278 | 16 | ssodata.SignatureMethod = "PLAINTEXT" | 24 | suite.request, _ = http.NewRequest("GET", baseUrl, nil) |
279 | 17 | err := ssodata.SignRequest(request) | 25 | } |
280 | 18 | c.Assert(err, IsNil) | 26 | |
281 | 19 | authHeader := request.Header["Authorization"][0] | 27 | // It is possible to sign a request with oauth_signature_method = PLAINTEXT |
282 | 28 | func (suite *OAuthTestSuite) TestSignRequestPlainText(c *C) { | ||
283 | 29 | suite.rp.SignatureMethod = PLAINTEXT{} | ||
284 | 30 | err := suite.ssodata.SignRequest(&suite.rp, suite.request) | ||
285 | 31 | if err != nil { | ||
286 | 32 | c.Log(err) | ||
287 | 33 | c.FailNow() | ||
288 | 34 | } | ||
289 | 35 | authHeader := suite.request.Header["Authorization"][0] | ||
290 | 20 | c.Assert(authHeader, Matches, `^OAuth.*`) | 36 | c.Assert(authHeader, Matches, `^OAuth.*`) |
291 | 21 | c.Assert(authHeader, Matches, `.*realm="API".*`) | 37 | c.Assert(authHeader, Matches, `.*realm="API".*`) |
292 | 22 | c.Assert(authHeader, Matches, | 38 | c.Assert(authHeader, Matches, |
294 | 23 | `.*oauth_consumer_key="`+url.QueryEscape(ssodata.ConsumerKey)+`".*`) | 39 | `.*oauth_consumer_key="`+url.QueryEscape( |
295 | 40 | suite.ssodata.ConsumerKey)+`".*`) | ||
296 | 24 | c.Assert(authHeader, Matches, | 41 | c.Assert(authHeader, Matches, |
298 | 25 | `.*oauth_token="`+url.QueryEscape(ssodata.TokenKey)+`".*`) | 42 | `.*oauth_token="`+url.QueryEscape(suite.ssodata.TokenKey)+`".*`) |
299 | 26 | c.Assert(authHeader, Matches, | 43 | c.Assert(authHeader, Matches, |
300 | 27 | `.*oauth_signature="`+url.QueryEscape( | 44 | `.*oauth_signature="`+url.QueryEscape( |
302 | 28 | ssodata.ConsumerSecret+`&`+ssodata.TokenSecret)+`.*`) | 45 | suite.ssodata.ConsumerSecret)+`&`+url.QueryEscape( |
303 | 46 | suite.ssodata.TokenSecret)+`.*`) | ||
304 | 29 | } | 47 | } |
305 | 30 | 48 | ||
319 | 31 | // Test the request signing with oauth_signature_method = SHA1 | 49 | // It is possible to sign a request with oauth_signature_method = SHA1 |
320 | 32 | func (suite *USSOTestSuite) TestSignRequestSHA1(c *C) { | 50 | func (suite *OAuthTestSuite) TestSignRequestSHA1(c *C) { |
321 | 33 | baseUrl := "https://localhost" | 51 | suite.rp.SignatureMethod = HMACSHA1{} |
322 | 34 | ssodata := SSOData{BaseURL: baseUrl, ConsumerKey: consumerKey, | 52 | err := suite.ssodata.SignRequest(&suite.rp, suite.request) |
323 | 35 | ConsumerSecret: consumerSecret, TokenKey: tokenKey, | 53 | if err != nil { |
324 | 36 | TokenName: tokenName, TokenSecret: tokenSecret, | 54 | c.Log(err) |
325 | 37 | Nonce: "10888885", Timestamp: "1358853126"} | 55 | c.FailNow() |
326 | 38 | request, _ := http.NewRequest("GET", baseUrl, nil) | 56 | } |
327 | 39 | ssodata.HTTPMethod = "GET" | 57 | authHeader := suite.request.Header["Authorization"][0] |
315 | 40 | ssodata.SignatureMethod = "HMAC-SHA1" | ||
316 | 41 | err := ssodata.SignRequest(request) | ||
317 | 42 | c.Assert(err, IsNil) | ||
318 | 43 | authHeader := request.Header["Authorization"][0] | ||
328 | 44 | c.Assert(authHeader, Matches, `^OAuth.*`) | 58 | c.Assert(authHeader, Matches, `^OAuth.*`) |
329 | 45 | c.Assert(authHeader, Matches, `.*realm="API".*`) | 59 | c.Assert(authHeader, Matches, `.*realm="API".*`) |
330 | 46 | c.Assert(authHeader, Matches, | 60 | c.Assert(authHeader, Matches, |
332 | 47 | `.*oauth_consumer_key="`+url.QueryEscape(ssodata.ConsumerKey)+`".*`) | 61 | `.*oauth_consumer_key="`+url.QueryEscape( |
333 | 62 | suite.ssodata.ConsumerKey)+`".*`) | ||
334 | 48 | c.Assert(authHeader, Matches, | 63 | c.Assert(authHeader, Matches, |
336 | 49 | `.*oauth_token="`+url.QueryEscape(ssodata.TokenKey)+`".*`) | 64 | `.*oauth_token="`+url.QueryEscape(suite.ssodata.TokenKey)+`".*`) |
337 | 50 | c.Assert(authHeader, Matches, | 65 | c.Assert(authHeader, Matches, |
338 | 51 | `.*oauth_signature="`+"amJnYeek4G9ObTgTiE2y6cwTyPg="+`.*`) | 66 | `.*oauth_signature="`+"amJnYeek4G9ObTgTiE2y6cwTyPg="+`.*`) |
339 | 52 | } | 67 | } |
340 | 53 | 68 | ||
341 | === modified file 'url.go' | |||
342 | --- url.go 2013-01-29 11:23:37 +0000 | |||
343 | +++ url.go 2013-02-05 12:38:20 +0000 | |||
344 | @@ -3,43 +3,60 @@ | |||
345 | 3 | import ( | 3 | import ( |
346 | 4 | "fmt" | 4 | "fmt" |
347 | 5 | "net/url" | 5 | "net/url" |
348 | 6 | "sort" | ||
349 | 6 | "strings" | 7 | "strings" |
350 | 7 | ) | 8 | ) |
351 | 8 | 9 | ||
352 | 9 | // Remove the standard ports from the URL. | 10 | // Remove the standard ports from the URL. |
355 | 10 | func normalizeHost(scheme, host_spec string) string { | 11 | func normalizeHost(scheme, hostSpec string) string { |
356 | 11 | standard_ports := map[string]string{ | 12 | standardPorts := map[string]string{ |
357 | 12 | "http": "80", | 13 | "http": "80", |
358 | 13 | "https": "443", | 14 | "https": "443", |
359 | 14 | } | 15 | } |
362 | 15 | host_parts := strings.Split(host_spec, ":") | 16 | hostParts := strings.Split(hostSpec, ":") |
363 | 16 | if len(host_parts) == 2 && host_parts[1] == standard_ports[scheme] { | 17 | if len(hostParts) == 2 && hostParts[1] == standardPorts[scheme] { |
364 | 17 | // There's a port, but it's the default one. Leave it out. | 18 | // There's a port, but it's the default one. Leave it out. |
366 | 18 | return host_parts[0] | 19 | return hostParts[0] |
367 | 19 | } | 20 | } |
369 | 20 | return host_spec | 21 | return hostSpec |
370 | 21 | } | 22 | } |
371 | 22 | 23 | ||
372 | 23 | // Normalize the URL according to OAuth specs. | 24 | // Normalize the URL according to OAuth specs. |
375 | 24 | func NormalizeURL(input_url string) (string, error) { | 25 | func NormalizeURL(inputUrl string) (string, error) { |
376 | 25 | parsed_url, err := url.Parse(input_url) | 26 | parsedUrl, err := url.Parse(inputUrl) |
377 | 26 | if err != nil { | 27 | if err != nil { |
378 | 27 | return "", err | 28 | return "", err |
379 | 28 | } | 29 | } |
380 | 29 | 30 | ||
385 | 30 | host := normalizeHost(parsed_url.Scheme, parsed_url.Host) | 31 | host := normalizeHost(parsedUrl.Scheme, parsedUrl.Host) |
386 | 31 | normalized_url := fmt.Sprintf( | 32 | normalizedUrl := fmt.Sprintf( |
387 | 32 | "%v://%v%v", parsed_url.Scheme, host, parsed_url.Path) | 33 | "%v://%v%v", parsedUrl.Scheme, host, parsedUrl.Path) |
388 | 33 | return normalized_url, nil | 34 | return normalizedUrl, nil |
389 | 34 | } | 35 | } |
390 | 35 | 36 | ||
391 | 36 | // Normalize the parameters in the query string according to OAuth specs. | 37 | // Normalize the parameters in the query string according to OAuth specs. |
392 | 38 | // url.Values.Encode encoded the GET parameters in a consistent order | ||
393 | 39 | // we do the encoding ourselves. | ||
394 | 37 | func NormalizeParameters(parameters url.Values) (string, error) { | 40 | func NormalizeParameters(parameters url.Values) (string, error) { |
402 | 38 | filtered_map := make(url.Values, len(parameters)) | 41 | filteredMap := make(url.Values, len(parameters)) |
403 | 39 | for param, value := range parameters { | 42 | keys := make([]string, len(parameters)) |
404 | 40 | if param != "oauth_signature" { | 43 | i := 0 |
405 | 41 | filtered_map[param] = value | 44 | for key, _ := range parameters { |
406 | 42 | } | 45 | keys[i] = key |
407 | 43 | } | 46 | i++ |
408 | 44 | return filtered_map.Encode(), nil | 47 | } |
409 | 48 | sort.Strings(keys) | ||
410 | 49 | for _, key := range keys { | ||
411 | 50 | if key != "oauth_signature" { | ||
412 | 51 | filteredMap[key] = parameters[key] | ||
413 | 52 | } | ||
414 | 53 | } | ||
415 | 54 | parts := make([]string, 0, len(filteredMap)) | ||
416 | 55 | for _, key := range keys { | ||
417 | 56 | prefix := url.QueryEscape(key) + "=" | ||
418 | 57 | for _, v := range filteredMap[key] { | ||
419 | 58 | parts = append(parts, prefix+url.QueryEscape(v)) | ||
420 | 59 | } | ||
421 | 60 | } | ||
422 | 61 | return strings.Join(parts, "&"), nil | ||
423 | 45 | } | 62 | } |
424 | 46 | 63 | ||
425 | === modified file 'url_test.go' | |||
426 | --- url_test.go 2013-01-29 14:27:25 +0000 | |||
427 | +++ url_test.go 2013-02-05 12:38:20 +0000 | |||
428 | @@ -60,7 +60,7 @@ | |||
429 | 60 | output, err := NormalizeParameters( | 60 | output, err := NormalizeParameters( |
430 | 61 | url.Values{"a": []string{"1"}, "b": []string{"2"}}) | 61 | url.Values{"a": []string{"1"}, "b": []string{"2"}}) |
431 | 62 | c.Check(err, gocheck.Equals, nil) | 62 | c.Check(err, gocheck.Equals, nil) |
433 | 63 | c.Check(output, gocheck.Matches, "(a=1&b=2|b=2&a=1)") | 63 | c.Check(output, gocheck.Matches, "(a=1&b=2)") |
434 | 64 | } | 64 | } |
435 | 65 | 65 | ||
436 | 66 | // NormalizeParameters() escapes the parameters correctly when encoding | 66 | // NormalizeParameters() escapes the parameters correctly when encoding |
437 | @@ -84,5 +84,5 @@ | |||
438 | 84 | } | 84 | } |
439 | 85 | output, err := NormalizeParameters(params) | 85 | output, err := NormalizeParameters(params) |
440 | 86 | c.Check(err, gocheck.Equals, nil) | 86 | c.Check(err, gocheck.Equals, nil) |
442 | 87 | c.Check(output, gocheck.Matches, "(a=1&z=26|z=26&a=1)") | 87 | c.Check(output, gocheck.Matches, "(a=1&z=26)") |
443 | 88 | } | 88 | } |
444 | 89 | 89 | ||
445 | === modified file 'usso.go' | |||
446 | --- usso.go 2013-01-29 11:23:37 +0000 | |||
447 | +++ usso.go 2013-02-05 12:38:20 +0000 | |||
448 | @@ -49,7 +49,7 @@ | |||
449 | 49 | "password": password, | 49 | "password": password, |
450 | 50 | "token_name": tokenName, | 50 | "token_name": tokenName, |
451 | 51 | } | 51 | } |
453 | 52 | json_credentials, err := json.Marshal(credentials) | 52 | jsonCredentials, err := json.Marshal(credentials) |
454 | 53 | if err != nil { | 53 | if err != nil { |
455 | 54 | log.Printf("Error: %s\n", err) | 54 | log.Printf("Error: %s\n", err) |
456 | 55 | return nil, err | 55 | return nil, err |
457 | @@ -57,7 +57,7 @@ | |||
458 | 57 | response, err := http.Post( | 57 | response, err := http.Post( |
459 | 58 | server.tokenURL(), | 58 | server.tokenURL(), |
460 | 59 | "application/json", | 59 | "application/json", |
462 | 60 | strings.NewReader(string(json_credentials))) | 60 | strings.NewReader(string(jsonCredentials))) |
463 | 61 | if err != nil { | 61 | if err != nil { |
464 | 62 | return nil, err | 62 | return nil, err |
465 | 63 | } | 63 | } |
466 | @@ -77,14 +77,16 @@ | |||
467 | 77 | 77 | ||
468 | 78 | // Returns all the Ubuntu SSO information related to this account. | 78 | // Returns all the Ubuntu SSO information related to this account. |
469 | 79 | func (server UbuntuSSOServer) GetAccounts(ssodata *SSOData) (string, error) { | 79 | func (server UbuntuSSOServer) GetAccounts(ssodata *SSOData) (string, error) { |
474 | 80 | ssodata.BaseURL = server.AccountsURL() + ssodata.ConsumerKey | 80 | rp := RequestParameters{ |
475 | 81 | ssodata.HTTPMethod = "GET" | 81 | BaseURL: server.AccountsURL() + ssodata.ConsumerKey, |
476 | 82 | ssodata.SignatureMethod = "HMAC-SHA1" | 82 | HTTPMethod: "GET", |
477 | 83 | request, err := http.NewRequest(ssodata.HTTPMethod, ssodata.BaseURL, nil) | 83 | SignatureMethod: HMACSHA1{}} |
478 | 84 | |||
479 | 85 | request, err := http.NewRequest(rp.HTTPMethod, rp.BaseURL, nil) | ||
480 | 84 | if err != nil { | 86 | if err != nil { |
481 | 85 | return "", err | 87 | return "", err |
482 | 86 | } | 88 | } |
484 | 87 | err = SignRequest(ssodata, request) | 89 | err = SignRequest(ssodata, &rp, request) |
485 | 88 | if err != nil { | 90 | if err != nil { |
486 | 89 | return "", err | 91 | return "", err |
487 | 90 | } | 92 | } |
488 | @@ -103,12 +105,14 @@ | |||
489 | 103 | } | 105 | } |
490 | 104 | 106 | ||
491 | 105 | // Given oauth credentials and a request, return it signed. | 107 | // Given oauth credentials and a request, return it signed. |
494 | 106 | func SignRequest(ssodata *SSOData, request *http.Request) error { | 108 | func SignRequest( |
495 | 107 | return ssodata.SignRequest(request) | 109 | ssodata *SSOData, rp *RequestParameters, request *http.Request) error { |
496 | 110 | return ssodata.SignRequest(rp, request) | ||
497 | 108 | } | 111 | } |
498 | 109 | 112 | ||
499 | 110 | // Given oauth credentials return a valid http authorization header. | 113 | // Given oauth credentials return a valid http authorization header. |
502 | 111 | func GetAuthorizationHeader(ssodata *SSOData) (string, error) { | 114 | func GetAuthorizationHeader( |
503 | 112 | header, err := ssodata.GetAuthorizationHeader() | 115 | ssodata *SSOData, rp *RequestParameters) (string, error) { |
504 | 116 | header, err := ssodata.GetAuthorizationHeader(rp) | ||
505 | 113 | return header, err | 117 | return header, err |
506 | 114 | } | 118 | } |
Looks ok.