diff --git a/proxy/pkg/config/config.go b/proxy/pkg/config/config.go index 4077f9992..b1c61664d 100644 --- a/proxy/pkg/config/config.go +++ b/proxy/pkg/config/config.go @@ -142,6 +142,7 @@ type PolicySelector struct { Static *StaticSelectorConf Migration *MigrationSelectorConf Claims *ClaimsSelectorConf + Regex *RegexSelectorConf } // StaticSelectorConf is the config for the static-policy-selector @@ -173,6 +174,13 @@ type ClaimsSelectorConf struct { UnauthenticatedPolicy string `mapstructure:"unauthenticated_policy"` } +// RegexSelectorConf is the config for the regex-selector +type RegexSelectorConf struct { + DefaultPolicy string `mapstructure:"default_policy"` + MatchesPolicies map[string]map[string]string `mapstructure:"matches_policies"` + UnauthenticatedPolicy string `mapstructure:"unauthenticated_policy"` +} + // New initializes a new configuration func New() *Config { return &Config{ diff --git a/proxy/pkg/middleware/account_resolver.go b/proxy/pkg/middleware/account_resolver.go index d89ced260..12262a5b7 100644 --- a/proxy/pkg/middleware/account_resolver.go +++ b/proxy/pkg/middleware/account_resolver.go @@ -26,7 +26,7 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl "expires": int64(60), }) if err != nil { - logger.Fatal().Err(err).Msgf("Could not initialize token-manager") + logger.Fatal().Err(err).Msg("Could not initialize token-manager") } return &accountResolver{ @@ -53,8 +53,9 @@ type accountResolver struct { // TODO do not use the context to store values: https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39 func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) { - claims := oidc.FromContext(req.Context()) - u, ok := revauser.ContextGetUser(req.Context()) + ctx := req.Context() + claims := oidc.FromContext(ctx) + u, ok := revauser.ContextGetUser(ctx) if claims == nil && !ok { m.next.ServeHTTP(w, req) @@ -99,6 +100,10 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + // add user to context for selectors + ctx = revauser.ContextSetUser(ctx, u) + req = req.WithContext(ctx) + m.logger.Debug().Interface("claims", claims).Interface("user", u).Msg("associated claims with user") } @@ -107,7 +112,7 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) { m.logger.Error().Err(err).Msg("could not get owner scope") return } - token, err := m.tokenManager.MintToken(req.Context(), u, s) + token, err := m.tokenManager.MintToken(ctx, u, s) if err != nil { m.logger.Error().Err(err).Msg("could not mint token") w.WriteHeader(http.StatusInternalServerError) diff --git a/proxy/pkg/proxy/policy/selector.go b/proxy/pkg/proxy/policy/selector.go index 11abfe6f5..ac0d3f536 100644 --- a/proxy/pkg/proxy/policy/selector.go +++ b/proxy/pkg/proxy/policy/selector.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "net/http" + "regexp" "github.com/asim/go-micro/plugins/client/grpc/v3" + revauser "github.com/cs3org/reva/pkg/user" accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0" "github.com/owncloud/ocis/ocis-pkg/oidc" "github.com/owncloud/ocis/proxy/pkg/config" @@ -71,6 +73,10 @@ func LoadSelector(cfg *config.PolicySelector) (Selector, error) { return NewClaimsSelector(cfg.Claims), nil } + if cfg.Regex != nil { + return NewRegexSelector(cfg.Regex), nil + } + return nil, ErrUnexpectedConfigError } @@ -123,6 +129,15 @@ func NewMigrationSelector(cfg *config.MigrationSelectorConf, ss accounts.Account } // NewClaimsSelector selects the policy based on the "ocis.routing.policy" claim +// The policy for corner cases is configurable: +// "policy_selector": { +// "migration": { +// "default_policy" : "ocis", +// "unauthenticated_policy": "oc10" +// } +// }, +// +// 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, r *http.Request) (s string, err error) { if claims := oidc.FromContext(r.Context()); claims != nil { @@ -136,3 +151,69 @@ func NewClaimsSelector(cfg *config.ClaimsSelectorConf) Selector { return cfg.UnauthenticatedPolicy, nil } } + +// NewRegexSelector selects the policy based on a user property +// The policy for each case is configurable: +// "policy_selector": { +// "migration": { +// "matches_policies": { +// "mail": { +// "marie@example.com": "oc10" +// "[^@]+@example.com": "ocis" +// }, +// "username": { +// "(einstein|feynman)": "ocis" +// "marie": "oc10" +// }, +// "id": { +// "4c510ada-c86b-4815-8820-42cdf82c3d51": "ocis" +// "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c": "oc10" +// }, +// }, +// "unauthenticated_policy": "oc10" +// } +// }, +// +// This selector can be used in migration-scenarios where some users have already migrated from ownCloud10 to OCIS and +func NewRegexSelector(cfg *config.RegexSelectorConf) Selector { + var mailRegexPolicies map[*regexp.Regexp]string + for m, p := range cfg.MatchesPolicies["mail"] { + mailRegexPolicies[regexp.MustCompile(m)] = p + } + var usernameRegexPolicies map[*regexp.Regexp]string + for m, p := range cfg.MatchesPolicies["username"] { + usernameRegexPolicies[regexp.MustCompile(m)] = p + } + var idRegexPolicies map[*regexp.Regexp]string + for m, p := range cfg.MatchesPolicies["id"] { + usernameRegexPolicies[regexp.MustCompile(m)] = p + } + return func(ctx context.Context, r *http.Request) (s string, err error) { + if u, ok := revauser.ContextGetUser(ctx); ok { + if u.Mail != "" { + for r, p := range mailRegexPolicies { + if r.MatchString(u.Mail) { + return p, nil + } + } + } + if u.Username != "" { + for r, p := range usernameRegexPolicies { + if r.MatchString(u.Username) { + return p, nil + } + } + } + if u.Id != nil && u.Id.OpaqueId != "" { + for r, p := range idRegexPolicies { + if r.MatchString(u.Id.OpaqueId) { + return p, nil + } + } + } + return cfg.DefaultPolicy, nil + } + + return cfg.UnauthenticatedPolicy, nil + } +}