forked from mozilla-services/autograph
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathauthorize.go
181 lines (168 loc) · 5.67 KB
/
authorize.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent [email protected] [:ulfr]
package main
import (
"crypto/sha256"
"fmt"
"net/http"
"time"
log "github.com/sirupsen/logrus"
"go.mozilla.org/hawk"
)
// an authorization
type authorization struct {
ID string
Key string
Signers []string
HawkTimestampValidity string
hawkMaxTimestampSkew time.Duration
}
func abs(d time.Duration) time.Duration {
if d < 0 {
return -d
}
return d
}
// authorizeHeader validates the existence of the Authorization header as a hawk
// authorization header and makes sure that it is valid. It does not validate the
// body of the request, that is done within authorizeBody. This function returns
// the hawk auth struct, the userid, and an error which will indicate whether
// validation was successful.
func (a *autographer) authorizeHeader(r *http.Request) (auth *hawk.Auth, userid string, err error) {
if r.Header.Get("Authorization") == "" {
return nil, "", fmt.Errorf("missing Authorization header")
}
auth, err = hawk.ParseRequestHeader(r.Header.Get("Authorization"))
if a.stats != nil {
sendStatsErr := a.stats.Timing("hawk.header_parsed", time.Since(getRequestStartTime(r)), nil, 1.0)
if sendStatsErr != nil {
log.Warnf("Error sending hawk.header_parsed: %s", sendStatsErr)
}
}
if err != nil {
return nil, "", err
}
userid = auth.Credentials.ID
auth, err = hawk.NewAuthFromRequest(r, a.lookupCred(auth.Credentials.ID), a.lookupNonce)
if a.stats != nil {
sendStatsErr := a.stats.Timing("hawk.auth_created", time.Since(getRequestStartTime(r)), nil, 1.0)
if sendStatsErr != nil {
log.Warnf("Error sending hawk.auth_created: %s", sendStatsErr)
}
}
if err != nil {
return nil, "", err
}
hawk.MaxTimestampSkew = a.auths[userid].hawkMaxTimestampSkew
err = auth.Valid()
if a.stats != nil {
sendStatsErr := a.stats.Timing("hawk.validated", time.Since(getRequestStartTime(r)), nil, 1.0)
if sendStatsErr != nil {
log.Warnf("Error sending hawk.validated: %s", sendStatsErr)
}
skew := abs(auth.ActualTimestamp.Sub(auth.Timestamp))
sendStatsErr = a.stats.Timing("hawk.timestamp_skew", skew, nil, 1.0)
if sendStatsErr != nil {
log.Warnf("Error sending hawk.timestamp_skew: %s", sendStatsErr)
}
}
if err != nil {
return nil, "", err
}
return auth, userid, nil
}
// authorizeBody validates the body within the request and returns
// an error which will be nil if the authorization is successful
func (a *autographer) authorizeBody(auth *hawk.Auth, r *http.Request, body []byte) (err error) {
payloadhash := auth.PayloadHash(r.Header.Get("Content-Type"))
payloadhash.Write(body)
if a.stats != nil {
sendStatsErr := a.stats.Timing("hawk.payload_hashed", time.Since(getRequestStartTime(r)), nil, 1.0)
if sendStatsErr != nil {
log.Warnf("Error sending hawk.payload_hashed: %s", sendStatsErr)
}
}
if !auth.ValidHash(payloadhash) {
return fmt.Errorf("payload validation failed")
}
return nil
}
// authorize combines authorizeHeader and authorizeBody into one function.
func (a *autographer) authorize(r *http.Request, body []byte) (userid string, err error) {
auth, userid, err := a.authorizeHeader(r)
if err != nil {
return userid, err
}
err = a.authorizeBody(auth, r, body)
return userid, err
}
// lookupCred searches the authorizations for a user whose id matches the provided
// id string. If found, a Credential function is return to complete the hawk authorization.
// If not found, a function that returns an error is returned.
func (a *autographer) lookupCred(id string) hawk.CredentialsLookupFunc {
if _, ok := a.auths[id]; ok {
// matching user found, return its token
return func(creds *hawk.Credentials) error {
creds.Key = a.auths[id].Key
creds.Hash = sha256.New
return nil
}
}
// credentials not found, return a function that returns a CredentialError
return func(creds *hawk.Credentials) error {
return &hawk.CredentialError{
Type: hawk.UnknownID,
Credentials: &hawk.Credentials{
ID: id,
Key: "-",
Hash: sha256.New,
},
}
}
}
// lookupNonce searches the LRU cache for a previous nonce that matches the value provided in
// val. If found, this is a replay attack, and `false` is returned.
func (a *autographer) lookupNonce(val string, ts time.Time, creds *hawk.Credentials) bool {
if a.nonces.Contains(val) {
return false
}
a.nonces.Add(val, time.Now())
return true
}
// getSignerId returns the signer identifier for the user. If a keyid is specified,
// the corresponding signer is returned. If no signer is found, an error is returned
// and the signer identifier is set to -1.
func (a *autographer) getSignerID(userid, keyid string) (int, error) {
tag := userid + "+" + keyid
if _, ok := a.signerIndex[tag]; !ok {
if keyid == "" {
return -1, fmt.Errorf("%q does not have a default signing key", userid)
}
return -1, fmt.Errorf("%s is not authorized to sign with key ID %s", userid, keyid)
}
return a.signerIndex[tag], nil
}
func (a *autographer) PrintAuthorizations() {
fmt.Println("\n---- Signers ----")
for _, signr := range a.signers {
fmt.Printf("- %s [%s %s]:\n",
signr.Config().ID, signr.Config().Type, signr.Config().Mode)
for user, auth := range a.auths {
for _, authsigner := range auth.Signers {
if authsigner == signr.Config().ID {
fmt.Printf("\t* %s\n", user)
}
}
}
}
fmt.Println("\n---- Authorizations ----")
for user, auth := range a.auths {
fmt.Printf("-%s: \n", user)
for _, authsigner := range auth.Signers {
fmt.Printf("\t* %s\n", authsigner)
}
}
}