use cookie to enforce routing for regex and claim selector

This commit is contained in:
Willy Kloucek
2021-07-23 08:38:55 +00:00
committed by Jörn Friedrich Dreyer
parent cb70f4882f
commit a0dce56480
7 changed files with 129 additions and 7 deletions
+6
View File
@@ -221,6 +221,12 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic
middleware.AutoprovisionAccounts(cfg.AutoprovisionAccounts),
),
middleware.SelectorCookie(
middleware.Logger(l),
middleware.UserProvider(userProvider),
middleware.PolicySelectorConfig(*cfg.PolicySelector),
),
// finally, trigger home creation when a user logs in
middleware.CreateHome(
middleware.Logger(l),
+2
View File
@@ -174,6 +174,7 @@ type MigrationSelectorConf struct {
type ClaimsSelectorConf struct {
DefaultPolicy string `mapstructure:"default_policy"`
UnauthenticatedPolicy string `mapstructure:"unauthenticated_policy"`
SelectorCookieName string `mapstructure:"selector_cookie_name"`
}
// RegexSelectorConf is the config for the regex-selector
@@ -181,6 +182,7 @@ type RegexSelectorConf struct {
DefaultPolicy string `mapstructure:"default_policy"`
MatchesPolicies []RegexRuleConf `mapstructure:"matches_policies"`
UnauthenticatedPolicy string `mapstructure:"unauthenticated_policy"`
SelectorCookieName string `mapstructure:"selector_cookie_name"`
}
type RegexRuleConf struct {
Priority int `mapstructure:"priority"`
+11 -1
View File
@@ -1,10 +1,11 @@
package middleware
import (
"github.com/owncloud/ocis/proxy/pkg/user/backend"
"net/http"
"time"
"github.com/owncloud/ocis/proxy/pkg/user/backend"
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
@@ -23,6 +24,8 @@ type Options struct {
Logger log.Logger
// TokenManagerConfig for communicating with the reva token manager
TokenManagerConfig config.TokenManager
// PolicySelectorConfig for using the policy selector
PolicySelector config.PolicySelector
// HTTPClient to use for communication with the oidcAuth provider
HTTPClient *http.Client
// AccountsClient for resolving accounts
@@ -82,6 +85,13 @@ func TokenManagerConfig(cfg config.TokenManager) Option {
}
}
// PolicySelectorConfig provides a function to set the policy selector config option.
func PolicySelectorConfig(cfg config.PolicySelector) Option {
return func(o *Options) {
o.PolicySelector = cfg
}
}
// HTTPClient provides a function to set the http client config option.
func HTTPClient(c *http.Client) Option {
return func(o *Options) {
+78
View File
@@ -0,0 +1,78 @@
package middleware
import (
"net/http"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/oidc"
"github.com/owncloud/ocis/proxy/pkg/config"
"github.com/owncloud/ocis/proxy/pkg/proxy/policy"
)
// SelectorCookie provides a middleware which
func SelectorCookie(optionSetters ...Option) func(next http.Handler) http.Handler {
options := newOptions(optionSetters...)
logger := options.Logger
policySelector := options.PolicySelector
return func(next http.Handler) http.Handler {
return &selectorCookie{
next: next,
logger: logger,
policySelector: policySelector,
}
}
}
type selectorCookie struct {
next http.Handler
logger log.Logger
policySelector config.PolicySelector
}
func (m selectorCookie) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if m.policySelector.Regex == nil && m.policySelector.Claims == nil {
// only set selector cookie for regex and claim selectors
m.next.ServeHTTP(w, req)
return
}
ctx := req.Context()
claims := oidc.FromContext(ctx)
selectorCookieName := ""
if m.policySelector.Regex != nil {
selectorCookieName = m.policySelector.Regex.SelectorCookieName
} else if m.policySelector.Claims != nil {
selectorCookieName = m.policySelector.Claims.SelectorCookieName
}
_, err := req.Cookie(selectorCookieName)
if err != nil {
// no cookie there - try to add one
if claims != nil {
selectorFunc, err := policy.LoadSelector(&m.policySelector)
if err != nil {
m.logger.Err(err)
}
selector, err := selectorFunc(ctx, req)
if err != nil {
m.logger.Err(err)
}
cookie := http.Cookie{
Name: selectorCookieName,
Value: selector,
Domain: req.Host,
Path: "/",
MaxAge: 60 * 60,
HttpOnly: true,
}
http.SetCookie(w, &cookie)
}
}
m.next.ServeHTTP(w, req)
}
+30 -5
View File
@@ -3,6 +3,7 @@ package policy
import (
"context"
"fmt"
"net/http"
"regexp"
"sort"
@@ -22,6 +23,10 @@ var (
ErrUnexpectedConfigError = fmt.Errorf("could not initialize policy-selector for given config")
)
const (
SelectorCookieName = "owncloud-selector"
)
// Selector is a function which selects a proxy-policy based on the request.
//
// A policy is a random name which identifies a set of proxy-routes:
@@ -47,7 +52,7 @@ var (
// }
// ]
//}
type Selector func(ctx context.Context) (string, error)
type Selector func(ctx context.Context, r *http.Request) (string, error)
// LoadSelector constructs a specific policy-selector from a given configuration
func LoadSelector(cfg *config.PolicySelector) (Selector, error) {
@@ -84,10 +89,16 @@ func LoadSelector(cfg *config.PolicySelector) (Selector, error) {
}
if cfg.Claims != nil {
if cfg.Claims.SelectorCookieName == "" {
cfg.Claims.SelectorCookieName = SelectorCookieName
}
return NewClaimsSelector(cfg.Claims), nil
}
if cfg.Regex != nil {
if cfg.Regex.SelectorCookieName == "" {
cfg.Regex.SelectorCookieName = SelectorCookieName
}
return NewRegexSelector(cfg.Regex), nil
}
@@ -102,7 +113,7 @@ func LoadSelector(cfg *config.PolicySelector) (Selector, error) {
// "static": {"policy" : "ocis"}
// },
func NewStaticSelector(cfg *config.StaticSelectorConf) Selector {
return func(ctx context.Context) (s string, err error) {
return func(ctx context.Context, r *http.Request) (s string, err error) {
return cfg.Policy, nil
}
}
@@ -121,7 +132,7 @@ func NewStaticSelector(cfg *config.StaticSelectorConf) Selector {
// thus have an entry in ocis-accounts. All users without accounts entry are routed to the legacy ownCloud10 instance.
func NewMigrationSelector(cfg *config.MigrationSelectorConf, ss accounts.AccountsService) Selector {
var acc = ss
return func(ctx context.Context) (s string, err error) {
return func(ctx context.Context, r *http.Request) (s string, err error) {
var claims map[string]interface{}
if claims = oidc.FromContext(ctx); claims == nil {
return cfg.UnauthenticatedPolicy, nil
@@ -153,7 +164,14 @@ func NewMigrationSelector(cfg *config.MigrationSelectorConf, ss accounts.Account
//
// This selector can be used in migration-scenarios where some users have already migrated from ownCloud10 to OCIS and
func NewClaimsSelector(cfg *config.ClaimsSelectorConf) Selector {
return func(ctx context.Context) (s string, err error) {
return func(ctx context.Context, r *http.Request) (s string, err error) {
// use cookie first if provided
selectorCookie, err := r.Cookie(cfg.SelectorCookieName)
if err == nil {
return selectorCookie.Value, nil
}
// if no cookie is present, try to route by selector
if claims := oidc.FromContext(ctx); claims != nil {
if p, ok := claims[oidc.OcisRoutingPolicy].(string); ok && p != "" {
// TODO check we know the routing policy?
@@ -195,7 +213,14 @@ func NewRegexSelector(cfg *config.RegexSelectorConf) Selector {
policy: cfg.MatchesPolicies[i].Policy,
})
}
return func(ctx context.Context) (s string, err error) {
return func(ctx context.Context, r *http.Request) (s string, err error) {
// use cookie first if provided
selectorCookie, err := r.Cookie(cfg.SelectorCookieName)
if err == nil {
return selectorCookie.Value, nil
}
// if no cookie is present, try to route by selector
if u, ok := revauser.ContextGetUser(ctx); ok {
for i := range regexRules {
switch regexRules[i].property {
+1 -1
View File
@@ -109,7 +109,7 @@ func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
}
func (p *MultiHostReverseProxy) directorSelectionDirector(r *http.Request) {
pol, err := p.PolicySelector(r.Context())
pol, err := p.PolicySelector(r.Context(), r)
if err != nil {
p.logger.Error().Msgf("Error while selecting pol %v", err)
return