mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-04 01:39:16 -05:00
ugly working draft
This commit is contained in:
@@ -3,6 +3,7 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -48,8 +49,17 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
}
|
||||
cfg.PreSignedURL.AllowedHTTPMethods = ctx.StringSlice("presignedurl-allow-method")
|
||||
|
||||
// When running on single binary mode the before hook from the root command won't get called. We manually
|
||||
// call this before hook from ocis command, so the configuration can be loaded.
|
||||
cfg.Reva.Middleware.Auth.CredentialsByUserAgent = make(map[string]string, 0)
|
||||
uaw := ctx.StringSlice("proxy-user-agent-whitelist")
|
||||
for _, v := range uaw {
|
||||
parts := strings.Split(v, ":")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("unexpected config value for user-agent whitelist: %v, expected format is userAgent:challenge", v)
|
||||
}
|
||||
|
||||
cfg.Reva.Middleware.Auth.CredentialsByUserAgent[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
return ParseConfig(ctx, cfg)
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
@@ -288,6 +298,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic
|
||||
middleware.EnableBasicAuth(cfg.EnableBasicAuth),
|
||||
middleware.AccountsClient(accountsClient),
|
||||
middleware.OIDCIss(cfg.OIDC.Issuer),
|
||||
middleware.CredentialsByUserAgent(cfg.Reva.Middleware.Auth.CredentialsByUserAgent),
|
||||
),
|
||||
middleware.SignedURLAuth(
|
||||
middleware.Logger(l),
|
||||
|
||||
@@ -80,7 +80,16 @@ var (
|
||||
|
||||
// Reva defines all available REVA configuration.
|
||||
type Reva struct {
|
||||
Address string
|
||||
Address string
|
||||
Middleware Middleware
|
||||
}
|
||||
|
||||
type Middleware struct {
|
||||
Auth Auth
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
CredentialsByUserAgent map[string]string
|
||||
}
|
||||
|
||||
// Cache is a TTL cache configuration.
|
||||
|
||||
@@ -248,8 +248,15 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
EnvVars: []string{"PROXY_ENABLE_BASIC_AUTH"},
|
||||
Destination: &cfg.EnableBasicAuth,
|
||||
},
|
||||
}
|
||||
|
||||
// Reva Middlewares Config
|
||||
&cli.StringSliceFlag{
|
||||
Name: "proxy-user-agent-whitelist", // TODO naming?
|
||||
Value: cli.NewStringSlice(""),
|
||||
Usage: "TODO",
|
||||
EnvVars: []string{"PROXY_MIDDLEWARE_AUTH_CREDENTIALS_BY_USER_AGENT"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ListProxyWithConfig applies the config to the list commands flags.
|
||||
|
||||
@@ -42,6 +42,7 @@ func Authentication(opts ...Option) func(next http.Handler) http.Handler {
|
||||
OIDCIss(options.OIDCIss),
|
||||
TokenCacheSize(options.UserinfoCacheSize),
|
||||
TokenCacheTTL(time.Second*time.Duration(options.UserinfoCacheTTL)),
|
||||
CredentialsByUserAgent(options.CredentialsByUserAgent),
|
||||
)
|
||||
|
||||
basic := BasicAuth(
|
||||
@@ -49,6 +50,7 @@ func Authentication(opts ...Option) func(next http.Handler) http.Handler {
|
||||
EnableBasicAuth(options.EnableBasicAuth),
|
||||
AccountsClient(options.AccountsClient),
|
||||
OIDCIss(options.OIDCIss),
|
||||
CredentialsByUserAgent(options.CredentialsByUserAgent),
|
||||
)
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
|
||||
@@ -37,21 +37,47 @@ func BasicAuth(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
if !h.isPublicLink(req) {
|
||||
for i := 0; i < len(ProxyWwwAuthenticate); i++ {
|
||||
if strings.Contains(req.RequestURI, fmt.Sprintf("/%v/", ProxyWwwAuthenticate[i])) {
|
||||
for k, v := range options.CredentialsByUserAgent {
|
||||
if strings.Contains(k, req.UserAgent()) {
|
||||
w.Header().Del("Www-Authenticate")
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", strings.Title(v), req.Host))
|
||||
goto OUT
|
||||
}
|
||||
}
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Basic", req.Host))
|
||||
}
|
||||
}
|
||||
}
|
||||
OUT:
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Del("Www-Authenticate")
|
||||
|
||||
account, ok := h.getAccount(req)
|
||||
|
||||
// touch is a user agent locking guard, when touched changes to true it indicates the User-Agent on the
|
||||
// request is configured to support only one challenge, it it remains untouched, there are no considera-
|
||||
// tions and we should write all available authentication challenges to the response.
|
||||
touch := false
|
||||
|
||||
if !ok {
|
||||
for i := 0; i < len(ProxyWwwAuthenticate); i++ {
|
||||
if strings.Contains(req.RequestURI, fmt.Sprintf("/%v/", ProxyWwwAuthenticate[i])) {
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Basic", req.Host))
|
||||
// if the request is bound to a user agent the locked write Www-Authenticate for such user
|
||||
for k, v := range options.CredentialsByUserAgent {
|
||||
if strings.Contains(k, req.UserAgent()) {
|
||||
w.Header().Del("Www-Authenticate")
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", strings.Title(v), req.Host))
|
||||
touch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if the request is not bound to any user agent, write all available challenges
|
||||
if !touch {
|
||||
writeSupportedAuthenticateHeader(w, req)
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -41,18 +41,19 @@ func OIDCAuth(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
// there is no bearer token on the request,
|
||||
if !h.shouldServe(req) {
|
||||
// oidc supported but token not present, add header and handover to the next middleware.
|
||||
|
||||
// TODO for this logic to work and we don't return superfluous Www-Authenticate headers we would need to
|
||||
// add Www-Authenticate only on selected endpoints, because Reva won't cleanup already written headers.
|
||||
// this means that requests such as:
|
||||
// curl -v -k -u admin:admin -H "depth: 0" -X PROPFIND https://localhost:9200/remote.php/dav/files | xmllint --format -
|
||||
// even when succeeding, will contain a Www-Authenticate header.
|
||||
|
||||
for i := 0; i < len(ProxyWwwAuthenticate); i++ {
|
||||
if strings.Contains(req.RequestURI, fmt.Sprintf("/%v/", ProxyWwwAuthenticate[i])) {
|
||||
for k, v := range options.CredentialsByUserAgent {
|
||||
if strings.Contains(k, req.UserAgent()) {
|
||||
w.Header().Del("Www-Authenticate")
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", strings.Title(v), req.Host))
|
||||
goto OUT
|
||||
}
|
||||
}
|
||||
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", "Bearer", req.Host))
|
||||
}
|
||||
}
|
||||
OUT:
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ type Options struct {
|
||||
UserinfoCacheSize int
|
||||
// UserinfoCacheTTL sets the max cache duration for the userinfo cache, intended for the oidc_auth middleware
|
||||
UserinfoCacheTTL time.Duration
|
||||
// CredentialsByUserAgent sets the auth challenges on a per user-agent basis
|
||||
CredentialsByUserAgent map[string]string
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -108,6 +110,13 @@ func OIDCIss(iss string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// CredentialsByUserAgent sets UserAgentChallenges.
|
||||
func CredentialsByUserAgent(v map[string]string) Option {
|
||||
return func(o *Options) {
|
||||
o.CredentialsByUserAgent = v
|
||||
}
|
||||
}
|
||||
|
||||
// RevaGatewayClient provides a function to set the the reva gateway service client option.
|
||||
func RevaGatewayClient(gc gateway.GatewayAPIClient) Option {
|
||||
return func(o *Options) {
|
||||
|
||||
Reference in New Issue
Block a user