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 3dc6701
Show file tree
Hide file tree
Showing 39 changed files with 285 additions and 244 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-20240425082555-eb411e7a0895
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-20240425082555-eb411e7a0895 h1:q+SEX+9BLRyYZywK7udI1037spKsgW3gElSXKUo26H4=
github.com/2403905/reva/v2 v2.0.0-20240425082555-eb411e7a0895/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.

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

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

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

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

Loading

0 comments on commit 3dc6701

Please sign in to comment.