github.com/BTBurke/caddy-jwt@v3.7.1+incompatible/config.go (about) 1 package jwt 2 3 import ( 4 "fmt" 5 6 "github.com/caddyserver/caddy" 7 "github.com/caddyserver/caddy/caddyhttp/httpserver" 8 ) 9 10 // RuleType distinguishes between ALLOW and DENY rules 11 type RuleType int 12 13 const ( 14 // ALLOW represents a rule that should allow access based on claim value 15 ALLOW RuleType = iota 16 17 // DENY represents a rule that should deny access based on claim value 18 DENY 19 ) 20 21 // EncryptionType specifies the valid configuration for a path 22 type EncryptionType int 23 24 const ( 25 // HS family of algorithms 26 HMAC EncryptionType = iota + 1 27 // RS and ES families of algorithms 28 PKI 29 ) 30 31 // Auth represents configuration information for the middleware 32 type Auth struct { 33 Rules []Rule 34 Next httpserver.Handler 35 Realm string 36 } 37 38 // Rule represents the configuration for a site 39 type Rule struct { 40 Path string 41 ExceptedPaths []string 42 AccessRules []AccessRule 43 Redirect string 44 AllowRoot bool 45 KeyBackends []KeyBackend 46 Passthrough bool 47 StripHeader bool 48 TokenSources []TokenSource 49 } 50 51 // AccessRule represents a single ALLOW/DENY rule based on the value of a claim in 52 // a validated token 53 type AccessRule struct { 54 Authorize RuleType 55 Claim string 56 Value string 57 } 58 59 func init() { 60 caddy.RegisterPlugin("jwt", caddy.Plugin{ 61 ServerType: "http", 62 Action: Setup, 63 }) 64 } 65 66 // Setup is called by Caddy to parse the config block 67 func Setup(c *caddy.Controller) error { 68 rules, err := parse(c) 69 if err != nil { 70 return err 71 } 72 73 c.OnStartup(func() error { 74 fmt.Println("JWT middleware is initiated") 75 return nil 76 }) 77 78 host := httpserver.GetConfig(c).Addr.Host 79 80 httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler { 81 return &Auth{ 82 Rules: rules, 83 Next: next, 84 Realm: host, 85 } 86 }) 87 88 return nil 89 } 90 91 func parse(c *caddy.Controller) ([]Rule, error) { 92 defaultKeyBackends, err := NewDefaultKeyBackends() 93 if err != nil { 94 return nil, err 95 } 96 97 // This parses the following config blocks 98 /* 99 jwt /hello 100 jwt /anotherpath 101 jwt { 102 path /hello 103 } 104 */ 105 var rules []Rule 106 for c.Next() { 107 args := c.RemainingArgs() 108 switch len(args) { 109 case 0: 110 // no argument passed, check the config block 111 112 var r = Rule{ 113 KeyBackends: defaultKeyBackends, 114 } 115 for c.NextBlock() { 116 switch c.Val() { 117 case "path": 118 if !c.NextArg() { 119 // we are expecting a value 120 return nil, c.ArgErr() 121 } 122 // return error if multiple paths in a block 123 if len(r.Path) != 0 { 124 return nil, c.ArgErr() 125 } 126 r.Path = c.Val() 127 if c.NextArg() { 128 // we are expecting only one value. 129 return nil, c.ArgErr() 130 } 131 case "except": 132 if !c.NextArg() { 133 return nil, c.ArgErr() 134 } 135 r.ExceptedPaths = append(r.ExceptedPaths, c.Val()) 136 if c.NextArg() { 137 // except only allows one path per declaration 138 return nil, c.ArgErr() 139 } 140 case "allowroot": 141 r.AllowRoot = true 142 case "allow": 143 args1 := c.RemainingArgs() 144 if len(args1) != 2 { 145 return nil, c.ArgErr() 146 } 147 r.AccessRules = append(r.AccessRules, AccessRule{Authorize: ALLOW, Claim: args1[0], Value: args1[1]}) 148 case "deny": 149 args1 := c.RemainingArgs() 150 if len(args1) != 2 { 151 return nil, c.ArgErr() 152 } 153 r.AccessRules = append(r.AccessRules, AccessRule{Authorize: DENY, Claim: args1[0], Value: args1[1]}) 154 case "redirect": 155 args1 := c.RemainingArgs() 156 if len(args1) != 1 { 157 return nil, c.ArgErr() 158 } 159 r.Redirect = args1[0] 160 case "publickey": 161 args1 := c.RemainingArgs() 162 if len(args1) != 1 { 163 return nil, c.ArgErr() 164 } 165 backend, err := NewLazyPublicKeyFileBackend(args1[0]) 166 if err != nil { 167 return nil, c.Err(err.Error()) 168 } 169 r.KeyBackends = append(r.KeyBackends, backend) 170 case "secret": 171 args1 := c.RemainingArgs() 172 if len(args1) != 1 { 173 return nil, c.ArgErr() 174 } 175 backend, err := NewLazyHmacKeyBackend(args1[0]) 176 if err != nil { 177 return nil, c.Err(err.Error()) 178 } 179 r.KeyBackends = append(r.KeyBackends, backend) 180 case "passthrough": 181 r.Passthrough = true 182 case "strip_header": 183 r.StripHeader = true 184 case "token_source": 185 args := c.RemainingArgs() 186 if len(args) < 1 { 187 return nil, c.ArgErr() 188 } 189 switch args[0] { 190 case "header": 191 var headerSource = &HeaderTokenSource{ 192 HeaderName: "Bearer", 193 } 194 if len(args) == 2 { 195 headerSource.HeaderName = args[1] 196 } else if len(args) > 2 { 197 return nil, c.ArgErr() 198 } 199 r.TokenSources = append(r.TokenSources, headerSource) 200 case "cookie": 201 if len(args) != 2 { 202 return nil, c.ArgErr() 203 } 204 r.TokenSources = append(r.TokenSources, &CookieTokenSource{ 205 CookieName: args[1], 206 }) 207 case "query_param": 208 if len(args) != 2 { 209 return nil, c.ArgErr() 210 } 211 r.TokenSources = append(r.TokenSources, &QueryTokenSource{ 212 ParamName: args[1], 213 }) 214 default: 215 return nil, c.Errf("unsupported token_source: '%s'", args[0]) 216 } 217 } 218 } 219 rules = append(rules, r) 220 case 1: 221 rules = append(rules, Rule{ 222 Path: args[0], 223 KeyBackends: defaultKeyBackends, 224 }) 225 // one argument passed 226 if c.NextBlock() { 227 // path specified, no block required. 228 return nil, c.ArgErr() 229 } 230 default: 231 // we want only one argument max 232 return nil, c.ArgErr() 233 } 234 } 235 236 // check all rules at least have a path and consistent encryption config 237 for _, r := range rules { 238 if r.Path == "" { 239 return nil, fmt.Errorf("Each rule must have a path") 240 } 241 var encType EncryptionType 242 for _, e := range r.KeyBackends { 243 switch e.(type) { 244 case *LazyHmacKeyBackend: 245 if encType > 0 && encType != HMAC { 246 return nil, fmt.Errorf("Configuration does not have a consistent encryption type for path %s. Cannot use both HMAC and PKI for a single path value.", r.Path) 247 } 248 encType = HMAC 249 case *LazyPublicKeyBackend: 250 if encType > 0 && encType != PKI { 251 return nil, fmt.Errorf("Configuration does not have a consistent encryption type for path %s. Cannot use both HMAC and PKI for a single path value.", r.Path) 252 } 253 encType = PKI 254 } 255 } 256 257 } 258 259 return rules, nil 260 }