@@ -5,27 +5,33 @@ import (
5
5
"fmt"
6
6
"net/http"
7
7
"os"
8
+ "strings"
8
9
9
10
"github.com/golang-jwt/jwt/v5"
10
11
)
11
12
12
- type Fallback string
13
+ type FallbackType string
13
14
14
15
const (
15
- FallbackError Fallback = "error"
16
- FallbackPass Fallback = "pass"
17
- FallbackIp Fallback = "ip"
18
- FallbackHeader Fallback = "header"
16
+ FallbackError FallbackType = "error"
17
+ FallbackPass FallbackType = "pass"
18
+ FallbackIp FallbackType = "ip"
19
+ FallbackHeader FallbackType = "header"
19
20
)
20
21
22
+ type Fallback struct {
23
+ Type FallbackType `yaml:"type,omitempty"`
24
+ Value string `yaml:"value,omitempty"`
25
+ KeepIfEmpty bool `yaml:"keepIfEmpty,omitempty"`
26
+ }
27
+
21
28
// Config the plugin configuration.
22
29
type Config struct {
23
- JwtHeaderName string `json:"jwt-header-name,omitempty"`
24
- JwtField string `json:"jwt-field,omitempty"`
25
- ValueHeaderName string `json:"value-header-name,omitempty"`
26
- FallbackType Fallback `json:"fallback-type,omitempty"`
27
- FallbackHeaderName string `json:"fallback-header-name,omitempty"`
28
- Debug bool `json:"debug,omitempty"`
30
+ JwtHeaderName string `yaml:"jwtHeaderName,omitempty"`
31
+ JwtField string `yaml:"jwtField,omitempty"`
32
+ ValueHeaderName string `yaml:"valueHeaderName,omitempty"`
33
+ Fallbacks []Fallback `yaml:"fallbacks,omitempty"`
34
+ Debug bool `yaml:"debug,omitempty"`
29
35
}
30
36
31
37
// CreateConfig creates the default plugin configuration.
@@ -56,7 +62,11 @@ func (a *Fifteen) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
56
62
return
57
63
}
58
64
59
- rawToken := req .Header .Get (a .cfg .JwtHeaderName )
65
+ rawHeader := req .Header .Get (a .cfg .JwtHeaderName )
66
+ rawToken := ""
67
+ if strings .HasPrefix (rawHeader , "Bearer " ) {
68
+ rawToken = rawHeader [len ("Bearer " ):]
69
+ }
60
70
parsedToken , _ , err := jwt .NewParser ().ParseUnverified (rawToken , jwt.MapClaims {})
61
71
if err != nil {
62
72
a .logDebug ("Could not parse non-empty jwt token, falling back: %s" , err .Error ())
@@ -84,26 +94,52 @@ func (a *Fifteen) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
84
94
return
85
95
}
86
96
} else {
87
- a .logDebug ("JWT field value has an unexpected type , falling back" )
97
+ a .logDebug ("JWT field value does not hold field %s , falling back" , a . cfg . JwtField )
88
98
a .ServeFallback (rw , req )
89
99
return
90
100
}
91
101
92
- a .next . ServeHTTP (rw , req )
102
+ a .end (rw , req )
93
103
}
94
104
95
105
func (a * Fifteen ) ServeFallback (rw http.ResponseWriter , req * http.Request ) {
96
- a .logDebug ("Fallbacked because JWT was not set, invalid or has unexpected value on field. Using fallback strategy: %s" , a .cfg .FallbackType )
97
- switch a .cfg .FallbackType {
98
- case FallbackError :
99
- rw .WriteHeader (http .StatusBadRequest )
100
- case FallbackIp :
101
- req .Header .Set (a .cfg .ValueHeaderName , req .RemoteAddr )
102
- case FallbackHeader :
103
- req .Header .Set (a .cfg .ValueHeaderName , req .Header .Get (a .cfg .FallbackHeaderName ))
104
- default :
105
- a .next .ServeHTTP (rw , req )
106
+ if len (a .cfg .Fallbacks ) == 0 {
107
+ a .logDebug ("Fallbacked because JWT was not set, invalid or has unexpected value on field. No fallback strategies, ignoring..." )
108
+ } else {
109
+ a .logDebug ("Fallbacked because JWT was not set, invalid or has unexpected value on field. Finding right fallback strategy" )
110
+ for i , fallback := range a .cfg .Fallbacks {
111
+ a .logDebug ("Strategy %d: %+v" , i , fallback )
112
+ var success bool
113
+ switch fallback .Type {
114
+ case FallbackError :
115
+ rw .Header ().Set ("Content-Type" , "text/plain" )
116
+ rw .WriteHeader (http .StatusBadRequest )
117
+ rw .Write ([]byte ("Bad request" ))
118
+ return
119
+ case FallbackPass :
120
+ a .logDebug ("Passing through" )
121
+ success = true
122
+ case FallbackIp :
123
+ req .Header .Set (a .cfg .ValueHeaderName , ipWithNoPort (req .RemoteAddr ))
124
+ success = true
125
+ case FallbackHeader :
126
+ headerValue := req .Header .Get (fallback .Value )
127
+ if headerValue == "" && ! fallback .KeepIfEmpty {
128
+ a .logDebug ("Header %s was empty, skipping..." , fallback .Value )
129
+ continue
130
+ }
131
+ req .Header .Set (a .cfg .ValueHeaderName , headerValue )
132
+ success = true
133
+ default :
134
+ a .logDebug ("Unknown fallback type, skipping..." )
135
+ }
136
+ if success {
137
+ a .logDebug ("Fallback strategy %d was successful" , i )
138
+ break
139
+ }
140
+ }
106
141
}
142
+ a .end (rw , req )
107
143
}
108
144
109
145
func (a * Fifteen ) logDebug (format string , args ... any ) {
@@ -112,3 +148,15 @@ func (a *Fifteen) logDebug(format string, args ...any) {
112
148
}
113
149
os .Stderr .WriteString ("[Fifteen middleware]: " + fmt .Sprintf (format , args ... ) + "\n " )
114
150
}
151
+
152
+ func (a * Fifteen ) end (rw http.ResponseWriter , req * http.Request ) {
153
+ a .logDebug ("ending with request headers: %+v" , req .Header )
154
+ a .next .ServeHTTP (rw , req )
155
+ }
156
+
157
+ func ipWithNoPort (addr string ) string {
158
+ if colon := strings .LastIndex (addr , ":" ); colon != - 1 {
159
+ return addr [:colon ]
160
+ }
161
+ return addr
162
+ }
0 commit comments