Skip to content

Commit

Permalink
Merge pull request #2248 from owncloud/claims-policy-selector
Browse files Browse the repository at this point in the history
add claims and regex policy selector
  • Loading branch information
butonic authored Jul 23, 2021
2 parents 1e4f833 + 07bb4a1 commit dcd8327
Show file tree
Hide file tree
Showing 14 changed files with 563 additions and 50 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/claims-policy-selector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Proxy: Add claims policy selector

Using the proxy config file, it is now possible to let let the IdP determine the routing policy by sending an `ocis.routing.policy` claim. Its value will be used to determine the set of routes for the logged in user.

https://github.com/owncloud/ocis/pull/2248
20 changes: 12 additions & 8 deletions ocis-pkg/oidc/claims.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package oidc

const (
Iss = "iss"
Sub = "sub"
Email = "email"
Name = "name"
Iss = "iss"
Sub = "sub"
Email = "email"
Name = "name"
PreferredUsername = "preferred_username"
UIDNumber = "uidnumber"
GIDNumber = "gidnumber"
Groups = "groups"
OwncloudUUID = "ownclouduuid"
UIDNumber = "uidnumber"
GIDNumber = "gidnumber"
Groups = "groups"
OwncloudUUID = "ownclouduuid"
OcisRoutingPolicy = "ocis.routing.policy"
)

// The ProviderMetadata describes an idp.
Expand Down Expand Up @@ -192,4 +193,7 @@ type StandardClaims struct {

// OcisID is a unique, persistent, non reassignable user id
OcisID string `json:"ownclouduuid,omitempty"`

// OcisRoutingPolicy is used to specify the routing policy to use for the ocis proxy
OcisRoutingPolicy string `json:"ocis.routing.policy,omitempty"`
}
5 changes: 3 additions & 2 deletions proxy/config/proxy-example-migration.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"HTTP": {
"Namespace": "com.owncloud"
"http": {
"addr": "0.0.0.0:9200",
"root": "/"
},
"oidc": {
"issuer": "https://localhost:9200",
Expand Down
168 changes: 168 additions & 0 deletions proxy/config/proxy-example-regex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
{
"http": {
"addr": "0.0.0.0:9200",
"root": "/"
},
"oidc": {
"issuer": "https://localhost:9200",
"insecure": true
},
"policy_selector": {
"regex": {
"selector_cookie_name": "owncloud-selector",
"default_policy": "oc10",
"matches_policies": [
{"priority": 10, "property": "mail", "match": "[email protected]", "policy": "ocis"},
{"priority": 20, "property": "mail", "match": "[^@][email protected]", "policy": "oc10"},
{"priority": 30, "property": "username", "match": "(einstein|feynman)", "policy": "ocis"},
{"priority": 40, "property": "username", "match": ".+", "policy": "oc10"},
{"priority": 50, "property": "id", "match": "4c510ada-c86b-4815-8820-42cdf82c3d51", "policy": "ocis"},
{"priority": 60, "property": "id", "match": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", "policy": "oc10"}
],
"unauthenticated_policy": "oc10"
}
},
"policies": [
{
"name": "ocis",
"routes": [
{
"endpoint": "/",
"backend": "http://localhost:9100"
},
{
"endpoint": "/.well-known/",
"backend": "http://localhost:9130"
},
{
"endpoint": "/konnect/",
"backend": "http://localhost:9130"
},
{
"endpoint": "/signin/",
"backend": "http://localhost:9130"
},
{
"type": "regex",
"endpoint": "/ocs/v[12].php/cloud/(users?|groups)",
"backend": "http://localhost:9110"
},
{
"endpoint": "/ocs/",
"backend": "http://localhost:9140"
},
{
"type": "query",
"endpoint": "/remote.php/?preview=1",
"backend": "http://localhost:9115"
},
{
"endpoint": "/remote.php/",
"backend": "http://localhost:9140"
},
{
"endpoint": "/dav/",
"backend": "http://localhost:9140"
},
{
"endpoint": "/webdav/",
"backend": "http://localhost:9140"
},
{
"endpoint": "/status.php",
"backend": "http://localhost:9140"
},
{
"endpoint": "/index.php/",
"backend": "http://localhost:9140"
},
{
"endpoint": "/data",
"backend": "http://localhost:9140"
},
{
"endpoint": "/graph/",
"backend": "http://localhost:9120"
},
{
"endpoint": "/graph-explorer/",
"backend": "http://localhost:9135"
},
{
"endpoint": "/api/v0/accounts",
"backend": "http://localhost:9181"
},
{
"endpoint": "/accounts.js",
"backend": "http://localhost:9181"
},
{
"endpoint": "/api/v0/settings",
"backend": "http://localhost:9190"
},
{
"endpoint": "/settings.js",
"backend": "http://localhost:9190"
},
{
"endpoint": "/onlyoffice.js",
"backend": "http://localhost:9220"
}
]
},
{
"name": "oc10",
"routes": [
{
"endpoint": "/",
"backend": "http://localhost:9100"
},
{
"endpoint": "/.well-known/",
"backend": "http://localhost:9130"
},
{
"endpoint": "/konnect/",
"backend": "http://localhost:9130"
},
{
"endpoint": "/signin/",
"backend": "http://localhost:9130"
},
{
"endpoint": "/ocs/",
"backend": "https://demo.owncloud.com",
"apache-vhost": true
},
{
"endpoint": "/remote.php/",
"backend": "https://demo.owncloud.com",
"apache-vhost": true
},
{
"endpoint": "/dav/",
"backend": "https://demo.owncloud.com",
"apache-vhost": true
},
{
"endpoint": "/webdav/",
"backend": "https://demo.owncloud.com",
"apache-vhost": true
},
{
"endpoint": "/status.php",
"backend": "https://demo.owncloud.com"
},
{
"endpoint": "/index.php/",
"backend": "https://demo.owncloud.com"
},
{
"endpoint": "/data",
"backend": "https://demo.owncloud.com",
"apache-vhost": true
}
]
}
]
}
5 changes: 3 additions & 2 deletions proxy/config/proxy-example.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"HTTP": {
"Namespace": "com.owncloud"
"http": {
"addr": "0.0.0.0:9200",
"root": "/"
},
"policy_selector": {
"static": {
Expand Down
6 changes: 6 additions & 0 deletions proxy/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
27 changes: 26 additions & 1 deletion proxy/pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package config

import "context"
import (
"context"
)

// Log defines the available logging configuration.
type Log struct {
Expand Down Expand Up @@ -141,6 +143,8 @@ type OIDC struct {
type PolicySelector struct {
Static *StaticSelectorConf
Migration *MigrationSelectorConf
Claims *ClaimsSelectorConf
Regex *RegexSelectorConf
}

// StaticSelectorConf is the config for the static-policy-selector
Expand All @@ -166,6 +170,27 @@ type MigrationSelectorConf struct {
UnauthenticatedPolicy string `mapstructure:"unauthenticated_policy"`
}

// ClaimsSelectorConf is the config for the claims-selector
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
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"`
Property string `mapstructure:"property"`
Match string `mapstructure:"match"`
Policy string `mapstructure:"policy"`
}

// New initializes a new configuration
func New() *Config {
return &Config{
Expand Down
2 changes: 1 addition & 1 deletion proxy/pkg/flagset/flagset.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
&cli.StringFlag{
Name: "user-cs3-claim",
Value: flags.OverrideDefaultString(cfg.UserCS3Claim, "mail"),
Usage: "The claim to use when looking up a user in the CS3 API, eg. 'userid' or 'mail'",
Usage: "The CS3 claim to use when looking up a user in the CS3 users API, eg. 'userid', 'username' or 'mail'",
EnvVars: []string{"PROXY_USER_CS3_CLAIM"},
Destination: &cfg.UserCS3Claim,
},
Expand Down
16 changes: 12 additions & 4 deletions proxy/pkg/middleware/account_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand All @@ -53,8 +53,10 @@ 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)
// TODO what if an X-Access-Token is set? happens eg for download requests to the /data endpoint in the reva frontend

if claims == nil && !ok {
m.next.ServeHTTP(w, req)
Expand Down Expand Up @@ -83,6 +85,8 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
m.logger.Debug().Interface("claims", claims).Msg("Autoprovisioning user")
u, err = m.userProvider.CreateUserFromClaims(req.Context(), claims)
// TODO instead of creating an account create a personal storage via the CS3 admin api?
// see https://cs3org.github.io/cs3apis/#cs3.admin.user.v1beta1.CreateUserRequest
}

if errors.Is(err, backend.ErrAccountDisabled) {
Expand All @@ -97,6 +101,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")
}

Expand All @@ -105,7 +113,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)
Expand Down
12 changes: 11 additions & 1 deletion proxy/pkg/middleware/options.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit dcd8327

Please sign in to comment.