-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmiddleware.go
231 lines (211 loc) · 6.09 KB
/
middleware.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package auth
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"net/http"
"strings"
"git.sr.ht/~mariusor/lw"
vocab "github.com/go-ap/activitypub"
"github.com/go-ap/errors"
"github.com/go-ap/filters"
"github.com/go-fed/httpsig"
"github.com/openshift/osin"
)
var AnonymousActor = vocab.Actor{
ID: vocab.PublicNS,
Type: vocab.ActorType,
Name: vocab.NaturalLanguageValues{
vocab.LangRefValue{
Ref: vocab.NilLangRef,
Value: vocab.Content("Anonymous"),
},
},
}
// readStore
type readStore interface {
// Load returns an Item or an ItemCollection from an IRI
Load(vocab.IRI, ...filters.Check) (vocab.Item, error)
}
type keyLoader struct {
loadActorFromKeyFn func(vocab.IRI) (*vocab.Actor, *vocab.PublicKey, error)
logFn LoggerFn
acc *vocab.Actor
}
func (k *keyLoader) GetKey(id string) (crypto.PublicKey, error) {
iri := vocab.IRI(id)
_, err := iri.URL()
if err != nil {
return nil, err
}
var act *vocab.Actor
var key *vocab.PublicKey
k.logFn(nil, "Loading Actor from Key IRI: %s", iri)
if act, key, err = k.loadActorFromKeyFn(iri); err != nil && !errors.IsNotModified(err) {
if errors.IsForbidden(err) {
return nil, err
}
return nil, errors.NewNotFound(err, "unable to find actor matching key id %s", iri)
}
if vocab.IsNil(act) {
return nil, errors.NotFoundf("unable to find actor matching key id %s", iri)
}
if !vocab.IsObject(act) {
return nil, errors.NotFoundf("unable to load actor matching key id %s, received %T", iri, act)
}
k.logFn(nil, "response received: %+s", act)
k.acc = act
if key == nil {
return nil, errors.NotFoundf("invalid key loaded %s for actor %s", iri, act.ID)
}
block, _ := pem.Decode([]byte(key.PublicKeyPem))
if block == nil {
return nil, errors.Newf("failed to parse PEM block containing the public key")
}
return x509.ParsePKIXPublicKey(block.Bytes)
}
type oauthStore interface {
readStore
LoadAccess(token string) (*osin.AccessData, error)
}
type oauthLoader struct {
logFn LoggerFn
acc *vocab.Actor
s oauthStore
}
func firstOrItem(it vocab.Item) (vocab.Item, error) {
if it.IsCollection() {
err := vocab.OnCollectionIntf(it, func(col vocab.CollectionInterface) error {
it = col.Collection().First()
return nil
})
if err != nil {
return nil, err
}
}
return it, nil
}
func unauthorized(err error) error {
return errors.NewUnauthorized(err, "Unable to validate actor from Bearer token")
}
func assertToBytes(in interface{}) ([]byte, error) {
var ok bool
var data string
if in == nil {
return nil, nil
} else if data, ok = in.(string); ok {
return []byte(data), nil
} else if byt, ok := in.([]byte); ok {
return byt, nil
} else if byt, ok := in.(json.RawMessage); ok {
return byt, nil
} else if str, ok := in.(fmt.Stringer); ok {
return []byte(str.String()), nil
}
return nil, errors.Errorf(`Could not assert "%v" to string`, in)
}
func (k *oauthLoader) Verify(r *http.Request) (error, string) {
bearer := osin.CheckBearerAuth(r)
if bearer == nil {
return errors.BadRequestf("could not load bearer token from request"), ""
}
dat, err := k.s.LoadAccess(bearer.Code)
if err != nil {
return err, ""
}
if dat == nil || dat.UserData == nil {
return errors.NotFoundf("unable to load bearer"), ""
}
if iri, err := assertToBytes(dat.UserData); err == nil {
it, err := k.s.Load(vocab.IRI(iri))
if err != nil {
return unauthorized(err), ""
}
if vocab.IsNil(it) {
return unauthorized(err), ""
}
if it, err = firstOrItem(it); err != nil {
return unauthorized(err), ""
}
err = vocab.OnActor(it, func(act *vocab.Actor) error {
k.acc = act
return nil
})
if err != nil {
return unauthorized(err), ""
}
} else {
return errors.Unauthorizedf("unable to load from bearer"), ""
}
return nil, ""
}
func verifyHTTPSignature(r *http.Request, keyGetter *keyLoader) error {
v, err := httpsig.NewVerifier(r)
if err != nil {
return errors.Annotatef(err, "unable to initialize HTTP Signatures verifier")
}
k, err := keyGetter.GetKey(v.KeyId())
if err != nil {
return errors.Annotatef(err, "unable to load public key based on signature")
}
algos := compatibleVerifyAlgorithms(k)
for _, algo := range algos {
if err = v.Verify(k, algo); err == nil {
return nil
}
keyGetter.logFn(nil, "Unable to verify request with %s %T: %+s", algo, k, err)
}
return errors.Newf("unable to verify HTTP Signature with any of the attempted algorithms: %v", algos)
}
func compatibleVerifyAlgorithms(pubKey crypto.PublicKey) []httpsig.Algorithm {
algos := make([]httpsig.Algorithm, 0)
switch pubKey.(type) {
case *rsa.PublicKey:
algos = append(algos, httpsig.RSA_SHA256, httpsig.RSA_SHA512)
case *ecdsa.PublicKey:
algos = append(algos, httpsig.ECDSA_SHA512, httpsig.ECDSA_SHA256)
case ed25519.PublicKey:
algos = append(algos, httpsig.ED25519)
}
return algos
}
func getAuthorization(hdr string) (string, string) {
pieces := strings.SplitN(hdr, " ", 2)
if len(pieces) < 2 {
return hdr, ""
}
return pieces[0], pieces[1]
}
// LoadActorFromRequest reads the Authorization header of an HTTP request and tries to decode it either
// an OAuth2 or HTTP Signatures:
//
// * For OAuth2 it tries to load the matching local actor and use it further in the processing logic.
// * For HTTP Signatures it tries to load the federated actor and use it further in the processing logic.
func (s *Server) LoadActorFromRequest(r *http.Request, toIgnore ...vocab.IRI) (vocab.Actor, error) {
// NOTE(marius): if the storage is nil, we can still use the remote client in the load function
var st oauthStore
if s.Server != nil && s.Server.Storage != nil {
st, _ = s.Server.Storage.(oauthStore)
}
isLocalFn := func(iri vocab.IRI) bool {
for _, i := range s.localURLs {
if iri.Contains(i, true) {
return true
}
}
return false
}
var logFn LoggerFn = func(ctx lw.Ctx, msg string, p ...interface{}) {
s.l.WithContext(ctx).Debugf(msg, p...)
}
ar := ClientResolver(s.cl,
SolverWithLogger(logFn), SolverWithStorage(st), SolverWithLocalIRIFn(isLocalFn),
SolverWithIgnoreList(toIgnore...),
)
return ar.LoadActorFromRequest(r)
}