-
Notifications
You must be signed in to change notification settings - Fork 20
/
handler.go
143 lines (119 loc) · 3.34 KB
/
handler.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
package handler
import (
"context"
"net/http"
"strings"
"github.com/Filecoin-Titan/titan/api"
"github.com/Filecoin-Titan/titan/api/types"
logging "github.com/ipfs/go-log/v2"
)
var log = logging.Logger("handler")
// ipLimiter = make(map[string]*rate.Limiter)
// mu sync.Mutex
// func getLimiter(ip string) *rate.Limiter {
// mu.Lock()
// defer mu.Unlock()
// limiter, exists := ipLimiter[ip]
// if !exists {
// limiter = rate.NewLimiter(2, 2)
// ipLimiter[ip] = limiter
// }
// return limiter
// }
type (
// RemoteAddr client address
RemoteAddr struct{}
// user id (node id)
ID struct{}
)
// Handler represents an HTTP handler that also adds remote client address and node ID to the request context
type Handler struct {
// handler *auth.Handler
verify func(ctx context.Context, token string) (*types.JWTPayload, error)
next http.HandlerFunc
}
// GetRemoteAddr returns the remote address of the client
func GetRemoteAddr(ctx context.Context) string {
v, ok := ctx.Value(RemoteAddr{}).(string)
if !ok {
return ""
}
return v
}
// GetNodeID returns the node ID of the client
func GetNodeID(ctx context.Context) string {
// check role
if !api.HasPerm(ctx, api.RoleDefault, api.RoleEdge) && !api.HasPerm(ctx, api.RoleDefault, api.RoleCandidate) {
log.Warnf("client is not edge and candidate")
return ""
}
v, ok := ctx.Value(ID{}).(string)
if !ok {
return ""
}
return v
}
// GetUserID returns the user ID of the client
func GetUserID(ctx context.Context) string {
// check role
if !api.HasPerm(ctx, api.RoleDefault, api.RoleUser) {
return ""
}
v, ok := ctx.Value(ID{}).(string)
if !ok {
return ""
}
return v
}
// New returns a new HTTP handler with the given auth handler and additional request context fields
func New(verify func(ctx context.Context, token string) (*types.JWTPayload, error), next http.HandlerFunc) http.Handler {
return &Handler{verify, next}
}
// ServeHTTP serves an HTTP request with the added client remote address and node ID in the request context
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
remoteAddr := r.Header.Get("X-Remote-Addr")
if remoteAddr == "" {
remoteAddr = r.RemoteAddr
}
ctx := r.Context()
ctx = context.WithValue(ctx, RemoteAddr{}, remoteAddr)
token := r.Header.Get("Authorization")
if token == "" {
token = r.FormValue("token")
if token != "" {
token = "Bearer " + token
}
}
if token != "" {
if !strings.HasPrefix(token, "Bearer ") {
log.Warn("missing Bearer prefix in auth header")
w.WriteHeader(401)
return
}
token = strings.TrimPrefix(token, "Bearer ")
payload, err := h.verify(ctx, token)
if err != nil {
log.Warnf("JWT Verification failed (originating from %s): %s, token:%s", r.RemoteAddr, err, token)
w.WriteHeader(401)
return
}
// host, _, err := net.SplitHostPort(remoteAddr)
// if err != nil {
// log.Warnf("SplitHostPort %s ", remoteAddr)
// w.WriteHeader(401)
// return
// }
// if payload.ID != "" {
// limiter := getLimiter(host)
// if !limiter.Allow() {
// log.Warnf("Too Many Requests %s, %s ", host, payload.ID)
// w.WriteHeader(http.StatusTooManyRequests)
// return
// }
// }
ctx = context.WithValue(ctx, ID{}, payload.ID)
ctx = api.WithPerm(ctx, payload.Allow)
ctx = api.WithUserAccessControl(ctx, payload.AccessControlList)
}
h.next(w, r.WithContext(ctx))
}