Skip to content

Commit

Permalink
Replacement for TokenInfo endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
2403905 committed Apr 25, 2024
1 parent baaf630 commit 9190a95
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 41 deletions.
13 changes: 13 additions & 0 deletions changelog/unreleased/tokenInfo-endpoint-replacement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Enhancement: Replacement for TokenInfo Endpoint


The client should basically always send a PROPFIND to /dav/public-files/{sharetoken}

* authenticated clients accessing an internal link are redirected to the "real" resource (`/dav/spaces/{target-resource-id}
* authenticated clients accessing a pubic link (password protected or not) for a resource they already have access to are also redirected to the "real" resource. (and always need to supply the password)
* unauthenticated clients accessing an internal link get a 401 returned with WWW-Authenticate set to Bearer (so that the client knows that it need to get a token via the IDP login page.
* unauthenticated clients accessing a password protected link get a 401 returned with WWW-Authenticate set to Basic to indicate the requirement for needing the link's password.

https://github.com/owncloud/ocis/pull/8926
https://github.com/cs3org/reva/pull/4653
https://github.com/owncloud/ocis/issues/8858
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,6 @@ replace github.com/egirna/icap-client => github.com/fschade/icap-client v0.0.0-2
// exclude the v2 line of go-sqlite3 which was released accidentally and prevents pulling in newer versions of go-sqlite3
// see https://github.com/mattn/go-sqlite3/issues/965 for more details
exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible

// remove before merge
replace github.com/cs3org/reva/v2 => github.com/2403905/reva/v2 v2.0.0-20240425102940-b42a20c71138
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/2403905/reva/v2 v2.0.0-20240425102940-b42a20c71138 h1:IvaQu0LJnKm2jwIfY+dQIm3mFNom8Igp7I03JVrnVzY=
github.com/2403905/reva/v2 v2.0.0-20240425102940-b42a20c71138/go.mod h1:GRUrOp5HbFVwZTgR9bVrMZ/MvVy+Jhxw1PdMmhhKP9E=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
Expand Down Expand Up @@ -1022,8 +1024,6 @@ github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781 h1:BUdwkIlf8IS2FasrrPg8gGPHQPOrQ18MS1Oew2tmGtY=
github.com/cs3org/go-cs3apis v0.0.0-20231023073225-7748710e0781/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY=
github.com/cs3org/reva/v2 v2.19.2-0.20240422150349-51ab7655f858 h1:cohKwOI/6UXXYhrjrZMYxw5GlM8wFS5445TZr/jmSzs=
github.com/cs3org/reva/v2 v2.19.2-0.20240422150349-51ab7655f858/go.mod h1:GRUrOp5HbFVwZTgR9bVrMZ/MvVy+Jhxw1PdMmhhKP9E=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
Expand Down
1 change: 0 additions & 1 deletion services/idp/tsconfig.tsbuildinfo

This file was deleted.

10 changes: 7 additions & 3 deletions services/proxy/pkg/command/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import (
"context"
"crypto/tls"
"fmt"
"github.com/owncloud/ocis/v2/services/proxy/pkg/staticroutes"
"net/http"
"os"
"time"

"github.com/owncloud/ocis/v2/services/proxy/pkg/staticroutes"

chimiddleware "github.com/go-chi/chi/v5/middleware"
"github.com/justinas/alice"
"github.com/oklog/run"
Expand Down Expand Up @@ -265,8 +266,9 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
if cfg.EnableBasicAuth {
logger.Warn().Msg("basic auth enabled, use only for testing or development")
authenticators = append(authenticators, middleware.BasicAuthenticator{
Logger: logger,
UserProvider: userProvider,
Logger: logger,
UserProvider: userProvider,
RevaGatewaySelector: gatewaySelector,
})
}

Expand All @@ -275,6 +277,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
RevaGatewaySelector: gatewaySelector,
})
authenticators = append(authenticators, middleware.NewOIDCAuthenticator(
gatewaySelector,
middleware.Logger(logger),
middleware.UserInfoCache(userInfoCache),
middleware.DefaultAccessTokenTTL(cfg.OIDC.UserinfoCache.TTL),
Expand Down Expand Up @@ -321,6 +324,7 @@ func loadMiddlewares(ctx context.Context, logger log.Logger, cfg *config.Config,
middleware.OIDCIss(cfg.OIDC.Issuer),
middleware.EnableBasicAuth(cfg.EnableBasicAuth),
middleware.TraceProvider(traceProvider),
middleware.WithRevaGatewaySelector(gatewaySelector),
),
middleware.AccountResolver(
middleware.Logger(logger),
Expand Down
59 changes: 57 additions & 2 deletions services/proxy/pkg/middleware/authentication.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
package middleware

import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"path"
"regexp"
"strings"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/conversions"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/services/proxy/pkg/router"
"github.com/owncloud/ocis/v2/services/proxy/pkg/webdav"
"go.opentelemetry.io/otel/trace"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"google.golang.org/grpc/metadata"
)

var (
Expand Down Expand Up @@ -57,6 +68,7 @@ func Authentication(auths []Authenticator, opts ...Option) func(next http.Handle
spanOpts := []trace.SpanStartOption{
trace.WithSpanKind(trace.SpanKindServer),
}
revaGatewaySelector := options.RevaGatewaySelector

return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -78,14 +90,14 @@ func Authentication(auths []Authenticator, opts ...Option) func(next http.Handle
return
}
}
if !isPublicPath(r.URL.Path) {
if !isPublicPath(r.URL.Path) || isInternal(r.URL.Path, revaGatewaySelector) {
// Failed basic authentication attempts receive the Www-Authenticate header in the response
var touch bool
caser := cases.Title(language.Und)
for k, v := range options.CredentialsByUserAgent {
if strings.Contains(k, r.UserAgent()) {
removeSuperfluousAuthenticate(w)
w.Header().Add("Www-Authenticate", fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", caser.String(v), r.Host))
w.Header().Add(WwwAuthenticate, fmt.Sprintf("%v realm=\"%s\", charset=\"UTF-8\"", caser.String(v), r.Host))
touch = true
break
}
Expand Down Expand Up @@ -209,3 +221,46 @@ func getTraceProvider(o Options) trace.TracerProvider {
}
return trace.NewNoopTracerProvider()
}

func isInternal(p string, selector pool.Selectable[gateway.GatewayAPIClient]) bool {
if strings.Contains(p, "public-files") {
client, err := selector.Next()
if err != nil {
return false
}

u, err := url.Parse(p)
if err != nil {
return false
}
token := path.Base(u.Path)

ctx := context.Background()

authResp, err := client.Authenticate(ctx, &gateway.AuthenticateRequest{
Type: authenticationType,
ClientId: token,
ClientSecret: "signature||",
})
if err == nil && authResp.GetStatus().GetCode() == 0 {

}
ctx = ctxpkg.ContextSetToken(ctx, authResp.Token)
ctx = ctxpkg.ContextSetUser(ctx, authResp.User)
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, authResp.Token)
sRes, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{
ResourceId: &provider.ResourceId{
StorageId: utils.PublicStorageProviderID,
SpaceId: utils.PublicStorageSpaceID,
OpaqueId: token,
},
}})
if err == nil && sRes.GetStatus().GetCode() == rpcv1beta1.Code_CODE_OK {
role := conversions.RoleFromResourcePermissions(sRes.Info.GetPermissionSet(), true)
if role.OCSPermissions() == 0 {
return true
}
}
}
return false
}
13 changes: 8 additions & 5 deletions services/proxy/pkg/middleware/basic_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@ package middleware
import (
"net/http"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
"github.com/owncloud/ocis/v2/services/proxy/pkg/user/backend"
)

// BasicAuthenticator is the authenticator responsible for HTTP Basic authentication.
type BasicAuthenticator struct {
Logger log.Logger
UserProvider backend.UserBackend
UserCS3Claim string
UserOIDCClaim string
Logger log.Logger
UserProvider backend.UserBackend
UserCS3Claim string
UserOIDCClaim string
RevaGatewaySelector pool.Selectable[gateway.GatewayAPIClient]
}

// Authenticate implements the authenticator interface to authenticate requests via basic auth.
func (m BasicAuthenticator) Authenticate(r *http.Request) (*http.Request, bool) {
if isPublicPath(r.URL.Path) {
if isPublicPath(r.URL.Path) && !isInternal(r.URL.Path, m.RevaGatewaySelector) {
// The authentication of public path requests is handled by another authenticator.
// Since we can't guarantee the order of execution of the authenticators, we better
// implement an early return here for paths we can't authenticate in this authenticator.
Expand Down
8 changes: 6 additions & 2 deletions services/proxy/pkg/middleware/oidc_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"strings"
"time"

gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/golang-jwt/jwt/v4"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/ocis-pkg/oidc"
Expand All @@ -23,7 +25,7 @@ const (
)

// NewOIDCAuthenticator returns a ready to use authenticator which can handle OIDC authentication.
func NewOIDCAuthenticator(opts ...Option) *OIDCAuthenticator {
func NewOIDCAuthenticator(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], opts ...Option) *OIDCAuthenticator {
options := newOptions(opts...)

return &OIDCAuthenticator{
Expand All @@ -36,6 +38,7 @@ func NewOIDCAuthenticator(opts ...Option) *OIDCAuthenticator {
AccessTokenVerifyMethod: options.AccessTokenVerifyMethod,
skipUserInfo: options.SkipUserInfo,
TimeFunc: time.Now,
RevaGatewaySelector: gatewaySelector,
}
}

Expand All @@ -50,6 +53,7 @@ type OIDCAuthenticator struct {
AccessTokenVerifyMethod string
skipUserInfo bool
TimeFunc func() time.Time
RevaGatewaySelector pool.Selectable[gateway.GatewayAPIClient]
}

func (m *OIDCAuthenticator) getClaims(token string, req *http.Request) (map[string]interface{}, error) {
Expand Down Expand Up @@ -168,7 +172,7 @@ func (m OIDCAuthenticator) shouldServe(req *http.Request) bool {
// Authenticate implements the authenticator interface to authenticate requests via oidc auth.
func (m *OIDCAuthenticator) Authenticate(r *http.Request) (*http.Request, bool) {
// there is no bearer token on the request,
if !m.shouldServe(r) || isPublicPath(r.URL.Path) {
if !m.shouldServe(r) || (isPublicPath(r.URL.Path) && !isInternal(r.URL.Path, m.RevaGatewaySelector)) {
// The authentication of public path requests is handled by another authenticator.
// Since we can't guarantee the order of execution of the authenticators, we better
// implement an early return here for paths we can't authenticate in this authenticator.
Expand Down
4 changes: 4 additions & 0 deletions services/proxy/pkg/middleware/public_share_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func (a PublicShareAuthenticator) Authenticate(r *http.Request) (*http.Request,
return nil, false
}

if isInternal(r.URL.Path, a.RevaGatewaySelector) {
return nil, false
}

query := r.URL.Query()
shareToken := r.Header.Get(headerShareToken)
if shareToken == "" {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 9190a95

Please sign in to comment.