-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmiddleware.go
86 lines (71 loc) · 2.2 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
/*
The AGPLv3 License (AGPLv3)
Copyright (c) 2023 Clément Joly
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"context"
"crypto/sha256"
"crypto/x509"
"fmt"
"log"
"git.sr.ht/~adnano/go-gemini"
)
func fingerprint(cert *x509.Certificate) string {
b := sha256.Sum256(cert.Raw)
s := fmt.Sprintf("%x", b)
return s
}
// To find various values in context
const userKey = iota
// UserMiddleware adds the user to context, found by its TLS certificate
type UserMiddleware struct {
db *SqliteDB
h gemini.Handler
}
func NewUserMiddleware(db *SqliteDB, h gemini.Handler) (*UserMiddleware, error) {
if db == nil {
return nil, fmt.Errorf(
"NewUserMiddleware: nil values not allowed",
)
}
return &UserMiddleware{db, h}, nil
}
func (um *UserMiddleware) ServeGemini(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) {
tls := r.TLS()
if len(tls.PeerCertificates) == 0 {
w.WriteHeader(gemini.StatusCertificateRequired, "Certificate required, but none provided")
return
}
fingerprint := fingerprint(tls.PeerCertificates[0])
user, err := um.db.GetUser(fingerprint)
if err == ErrUserNotFound {
w.WriteHeader(gemini.StatusCertificateNotAuthorized,
fmt.Sprintf(
"Unknown certificate, ask your admin to add yours: %q",
fingerprint,
))
return
}
if err != nil {
w.WriteHeader(gemini.StatusPermanentFailure, "Internal Error")
log.Printf("error getting user in db: %v", err)
return
}
ctx2 := context.WithValue(ctx, userKey, &user)
um.h.ServeGemini(ctx2, w, r)
}
func UserFromContext(ctx context.Context) (*User, bool) {
user, ok := ctx.Value(userKey).(*User)
return user, ok
}