1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-05-29 23:17:38 +02:00

Add request authorization ruleset

This commit is contained in:
Joel Speed 2020-07-19 09:37:06 +01:00 committed by Joel Speed
parent 333e68637f
commit 0ce9ae756e
No known key found for this signature in database
GPG Key ID: 6E80578D6751DEFB
3 changed files with 209 additions and 0 deletions

View File

@ -0,0 +1,17 @@
package options
type AuthorizationPolicy string
const (
AllowPolicy AuthorizationPolicy = "Allow"
DenyPolicy AuthorizationPolicy = "Deny"
)
type AuthorizationRule struct {
Policy AuthorizationPolicy
Path string
Methods []string
IPs []string
}
type RequestRules []AuthorizationRule

View File

@ -0,0 +1,86 @@
package authorization
import (
"errors"
"fmt"
"net"
"net/http"
"regexp"
"strings"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/ip"
)
type condition interface {
matches(*http.Request) bool
}
type methodCondition struct {
methods map[string]struct{}
}
func (m methodCondition) matches(req *http.Request) bool {
_, ok := m.methods[strings.ToUpper(req.Method)]
return ok
}
func newMethodCondition(methods []string) condition {
methodMap := make(map[string]struct{})
for _, method := range methods {
methodMap[strings.ToUpper(method)] = struct{}{}
}
return methodCondition{
methods: methodMap,
}
}
type pathCondition struct {
pathRegexp *regexp.Regexp
}
func (p pathCondition) matches(req *http.Request) bool {
return p.pathRegexp.MatchString(req.URL.Path)
}
func newPathCondition(path string) (condition, error) {
exp, err := regexp.Compile(path)
if err != nil {
return nil, err
}
return pathCondition{
pathRegexp: exp,
}, nil
}
type ipCondition struct {
netSet *ip.NetSet
getClientIP func(req *http.Request) net.IP
}
func (i ipCondition) matches(req *http.Request) bool {
ip := i.getClientIP(req)
if ip == nil {
return false
}
return i.netSet.Has(ip)
}
func newIPCondition(rawIPs []string, getClientIPFunc func(req *http.Request) net.IP) (condition, error) {
if getClientIPFunc == nil {
return nil, errors.New("client IP function required for IP condition")
}
netSet := ip.NewNetSet()
for _, rawIP := range rawIPs {
ipNet := ip.ParseIPNet(rawIP)
if ipNet == nil {
return nil, fmt.Errorf("could not parse IP network: %s", rawIP)
}
netSet.AddIPNet(*ipNet)
}
return ipCondition{
netSet: netSet,
getClientIP: getClientIPFunc,
}, nil
}

106
pkg/authorization/rules.go Normal file
View File

@ -0,0 +1,106 @@
package authorization
import (
"net"
"net/http"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
)
type AuthorizationPolicy int
const (
NonePolicy AuthorizationPolicy = iota
AllowPolicy
DenyPolicy
)
type RuleSet interface {
Matches(req *http.Request) AuthorizationPolicy
}
type rule struct {
conditions []condition
policy AuthorizationPolicy
}
func (r rule) matches(req *http.Request) AuthorizationPolicy {
for _, condition := range r.conditions {
if !condition.matches(req) {
// One of the conditions didn't match so this rule does not apply
return NonePolicy
}
}
// If all conditions match, return the configured rule policy
return r.policy
}
func newRule(authRule options.AuthorizationRule, getClientIPFunc func(*http.Request) net.IP) (rule, error) {
// This function should add the conditions in order of complexity, least complex first
conditions := []condition{}
if len(authRule.Methods) > 0 {
conditions = append(conditions, newMethodCondition(authRule.Methods))
}
if len(authRule.Path) > 0 {
condition, err := newPathCondition(authRule.Path)
if err != nil {
return rule{}, err
}
conditions = append(conditions, condition)
}
if len(authRule.IPs) > 0 {
condition, err := newIPCondition(authRule.IPs, getClientIPFunc)
if err != nil {
return rule{}, err
}
conditions = append(conditions, condition)
}
var policy AuthorizationPolicy
switch authRule.Policy {
case options.AllowPolicy:
policy = AllowPolicy
case options.DenyPolicy:
policy = DenyPolicy
default:
// This shouldn't be the case and should be prevented by validation
policy = NonePolicy
}
return rule{
conditions: conditions,
policy: policy,
}, nil
}
type ruleSet struct {
rules []rule
}
func (r ruleSet) Matches(req *http.Request) AuthorizationPolicy {
for _, rule := range r.rules {
if policy := rule.matches(req); policy != NonePolicy {
// The rule applies to this request, return its policy
return policy
}
}
// No rules matched
return NonePolicy
}
func NewRuleSet(requestRules options.RequestRules, getClientIPFunc func(*http.Request) net.IP) (RuleSet, error) {
rules := []rule{}
for _, requestRule := range requestRules {
r, err := newRule(requestRule, getClientIPFunc)
if err != nil {
return nil, err
}
rules = append(rules, r)
}
return ruleSet{
rules: rules,
}, nil
}