~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/usso/oauth.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENSE file for details.
 
3
 
 
4
package usso
 
5
 
 
6
import (
 
7
        "crypto/hmac"
 
8
        "crypto/sha1"
 
9
        "encoding/base64"
 
10
        "fmt"
 
11
        "math/rand"
 
12
        "net/http"
 
13
        "net/url"
 
14
        "strconv"
 
15
        "time"
 
16
)
 
17
 
 
18
// Initialize the random generator.
 
19
func init() {
 
20
        rand.Seed(time.Now().UTC().UnixNano())
 
21
}
 
22
 
 
23
// Create a timestamp used in authorization header.
 
24
func timestamp() string {
 
25
        return strconv.Itoa(int(time.Now().Unix()))
 
26
}
 
27
 
 
28
// Create a nonce used in authorization header.
 
29
func nonce() string {
 
30
        return strconv.Itoa(rand.Intn(100000000))
 
31
}
 
32
 
 
33
// Contains the oauth data to perform a request.
 
34
type SSOData struct {
 
35
        ConsumerKey    string `json:"consumer_key"`
 
36
        ConsumerSecret string `json:"consumer_secret"`
 
37
        Realm          string `json:"realm"`
 
38
        TokenKey       string `json:"token_key"`
 
39
        TokenName      string `json:"token_name"`
 
40
        TokenSecret    string `json:"token_secret"`
 
41
}
 
42
 
 
43
type RequestParameters struct {
 
44
        HTTPMethod      string
 
45
        BaseURL         string
 
46
        Params          url.Values
 
47
        Nonce           string
 
48
        Timestamp       string
 
49
        SignatureMethod SignatureMethod
 
50
}
 
51
 
 
52
type SignatureMethod interface {
 
53
        Name() string
 
54
        Signature(
 
55
                ssodata *SSOData, rp *RequestParameters) (string, error)
 
56
}
 
57
 
 
58
type PLAINTEXT struct{}
 
59
 
 
60
// Return the name of the signature method, used to compose the
 
61
// Authentication Header.
 
62
func (PLAINTEXT) Name() string { return "PLAINTEXT" }
 
63
 
 
64
// Calculate the oaut_signature part of the Authentication Header.
 
65
func (PLAINTEXT) Signature(
 
66
        ssodata *SSOData, rp *RequestParameters) (string, error) {
 
67
        return fmt.Sprintf(
 
68
                `%s&%s`,
 
69
                ssodata.ConsumerSecret,
 
70
                ssodata.TokenSecret), nil
 
71
}
 
72
 
 
73
type HMACSHA1 struct{}
 
74
 
 
75
// Return the name of the signature method, used to compose the
 
76
// Authentication Header.
 
77
func (HMACSHA1) Name() string { return "HMAC-SHA1" }
 
78
 
 
79
// Calculate the oaut_signature part of the Authentication Header.
 
80
func (HMACSHA1) Signature(
 
81
        ssodata *SSOData, rp *RequestParameters) (string, error) {
 
82
        baseUrl, err := NormalizeURL(rp.BaseURL)
 
83
        if err != nil {
 
84
                return "", err
 
85
        }
 
86
        query := url.Values{}
 
87
        for k, v := range rp.Params {
 
88
                query[k] = v
 
89
        }
 
90
        query.Set("oauth_consumer_key", ssodata.ConsumerKey)
 
91
        query.Set("oauth_nonce", rp.Nonce)
 
92
        query.Set("oauth_signature_method", string(rp.SignatureMethod.Name()))
 
93
        query.Set("oauth_timestamp", rp.Timestamp)
 
94
        query.Set("oauth_token", ssodata.TokenKey)
 
95
        query.Set("oauth_version", "1.0")
 
96
        params, err := NormalizeParameters(query)
 
97
        if err != nil {
 
98
                return "", err
 
99
        }
 
100
        baseString := fmt.Sprintf("%s&%s&%s",
 
101
                rp.HTTPMethod,
 
102
                escape(baseUrl),
 
103
                escape(params),
 
104
        )
 
105
        hashfun := hmac.New(sha1.New, []byte(
 
106
                ssodata.ConsumerSecret+"&"+ssodata.TokenSecret))
 
107
        hashfun.Write([]byte(baseString))
 
108
        rawsignature := hashfun.Sum(nil)
 
109
        base64signature := make(
 
110
                []byte, base64.StdEncoding.EncodedLen(len(rawsignature)))
 
111
        base64.StdEncoding.Encode(base64signature, rawsignature)
 
112
        return string(base64signature), nil
 
113
}
 
114
 
 
115
// Sign the provided request.
 
116
func (ssodata *SSOData) GetAuthorizationHeader(
 
117
        rp *RequestParameters) (string, error) {
 
118
        if rp.Nonce == "" {
 
119
                rp.Nonce = nonce()
 
120
        }
 
121
        if rp.Timestamp == "" {
 
122
                rp.Timestamp = timestamp()
 
123
        }
 
124
        signature, err := rp.SignatureMethod.Signature(ssodata, rp)
 
125
        if err != nil {
 
126
                return "", err
 
127
        }
 
128
        auth := fmt.Sprintf(
 
129
                `OAuth realm="%s", `+
 
130
                        `oauth_consumer_key="%s", `+
 
131
                        `oauth_token="%s", `+
 
132
                        `oauth_signature_method="%s", `+
 
133
                        `oauth_signature="%s", `+
 
134
                        `oauth_timestamp="%s", `+
 
135
                        `oauth_nonce="%s", `+
 
136
                        `oauth_version="1.0"`,
 
137
                url.QueryEscape(ssodata.Realm),
 
138
                url.QueryEscape(ssodata.ConsumerKey),
 
139
                url.QueryEscape(ssodata.TokenKey),
 
140
                rp.SignatureMethod.Name(),
 
141
                signature,
 
142
                url.QueryEscape(rp.Timestamp),
 
143
                url.QueryEscape(rp.Nonce))
 
144
 
 
145
        return auth, nil
 
146
}
 
147
 
 
148
// Sign the provided request.
 
149
func (ssodata *SSOData) SignRequest(
 
150
        rp *RequestParameters, req *http.Request) error {
 
151
        auth, error := ssodata.GetAuthorizationHeader(rp)
 
152
        if req.Header == nil {
 
153
                req.Header = make(map[string][]string)
 
154
        }
 
155
        req.Header.Add("Authorization", auth)
 
156
        return error
 
157
}