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  }