Merge branch 'master' into update-bridge-docs
This commit is contained in:
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/proxy/pkg/flagset"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Health is the entrypoint for the health command.
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/sync"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
ociscfg "github.com/owncloud/ocis/ocis-pkg/config"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
@@ -15,6 +14,7 @@ import (
|
||||
"github.com/owncloud/ocis/proxy/pkg/version"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/thejerf/suture/v4"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Execute is the entry point for the ocis-proxy command.
|
||||
|
||||
@@ -9,13 +9,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
chimiddleware "github.com/go-chi/chi/middleware"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/justinas/alice"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/oklog/run"
|
||||
acc "github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/conversions"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
pkgmiddleware "github.com/owncloud/ocis/ocis-pkg/middleware"
|
||||
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
|
||||
"github.com/owncloud/ocis/ocis-pkg/sync"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
@@ -30,6 +31,7 @@ import (
|
||||
"github.com/owncloud/ocis/proxy/pkg/user/backend"
|
||||
settings "github.com/owncloud/ocis/settings/pkg/proto/v0"
|
||||
storepb "github.com/owncloud/ocis/store/pkg/proto/v0"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
@@ -63,7 +65,7 @@ func Server(cfg *config.Config) *cli.Command {
|
||||
Action: func(c *cli.Context) error {
|
||||
logger := NewLogger(cfg)
|
||||
|
||||
if err := tracing.Configure(cfg, logger); err != nil {
|
||||
if err := tracing.Configure(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -151,14 +153,23 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic
|
||||
var userProvider backend.UserBackend
|
||||
switch cfg.AccountBackend {
|
||||
case "accounts":
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": cfg.TokenManager.JWTSecret,
|
||||
"expires": int64(24 * 60 * 60),
|
||||
})
|
||||
if err != nil {
|
||||
l.Error().Err(err).
|
||||
Msg("Failed to create token manager")
|
||||
}
|
||||
userProvider = backend.NewAccountsServiceUserBackend(
|
||||
acc.NewAccountsService("com.owncloud.api.accounts", grpc.DefaultClient),
|
||||
rolesClient,
|
||||
cfg.OIDC.Issuer,
|
||||
tokenManager,
|
||||
l,
|
||||
)
|
||||
case "cs3":
|
||||
userProvider = backend.NewCS3UserBackend(revaClient, rolesClient, revaClient, l)
|
||||
userProvider = backend.NewCS3UserBackend(rolesClient, revaClient, cfg.MachineAuthAPIKey, l)
|
||||
default:
|
||||
l.Fatal().Msgf("Invalid accounts backend type '%s'", cfg.AccountBackend)
|
||||
}
|
||||
@@ -182,6 +193,7 @@ func loadMiddlewares(ctx context.Context, l log.Logger, cfg *config.Config) alic
|
||||
|
||||
return alice.New(
|
||||
// first make sure we log all requests and redirect to https if necessary
|
||||
pkgmiddleware.TraceContext,
|
||||
chimiddleware.RealIP,
|
||||
chimiddleware.RequestID,
|
||||
middleware.AccessLog(l),
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/registry"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
tw "github.com/olekukonko/tablewriter"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/proxy/pkg/flagset"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// PrintVersion prints the service versions of all running instances.
|
||||
|
||||
@@ -70,7 +70,7 @@ type RouteType string
|
||||
const (
|
||||
// PrefixRoute are routes matched by a prefix
|
||||
PrefixRoute RouteType = "prefix"
|
||||
// QueryRoute are routes machted by a prefix and query parameters
|
||||
// QueryRoute are routes matched by a prefix and query parameters
|
||||
QueryRoute RouteType = "query"
|
||||
// RegexRoute are routes matched by a pattern
|
||||
RegexRoute RouteType = "regex"
|
||||
@@ -123,6 +123,7 @@ type Config struct {
|
||||
AccountBackend string
|
||||
UserOIDCClaim string
|
||||
UserCS3Claim string
|
||||
MachineAuthAPIKey string
|
||||
AutoprovisionAccounts bool
|
||||
EnableBasicAuth bool
|
||||
InsecureBackends bool
|
||||
|
||||
+13
-2
@@ -2,12 +2,23 @@ package cs3
|
||||
|
||||
import (
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
|
||||
proxytracing "github.com/owncloud/ocis/proxy/pkg/tracing"
|
||||
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func newConn(endpoint string) (*grpc.ClientConn, error) {
|
||||
conn, err := grpc.Dial(endpoint, grpc.WithInsecure())
|
||||
conn, err := grpc.Dial(
|
||||
endpoint,
|
||||
grpc.WithInsecure(),
|
||||
grpc.WithUnaryInterceptor(
|
||||
otelgrpc.UnaryClientInterceptor(
|
||||
otelgrpc.WithTracerProvider(
|
||||
proxytracing.TraceProvider,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ package flagset
|
||||
import (
|
||||
"path"
|
||||
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis/ocis-pkg/flags"
|
||||
pkgos "github.com/owncloud/ocis/ocis-pkg/os"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// RootWithConfig applies cfg to the root flagset
|
||||
@@ -69,28 +69,28 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
&cli.BoolFlag{
|
||||
Name: "tracing-enabled",
|
||||
Usage: "Enable sending traces",
|
||||
EnvVars: []string{"PROXY_TRACING_ENABLED"},
|
||||
EnvVars: []string{"PROXY_TRACING_ENABLED", "OCIS_TRACING_ENABLED"},
|
||||
Destination: &cfg.Tracing.Enabled,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-type",
|
||||
Value: flags.OverrideDefaultString(cfg.Tracing.Type, "jaeger"),
|
||||
Usage: "Tracing backend type",
|
||||
EnvVars: []string{"PROXY_TRACING_TYPE"},
|
||||
EnvVars: []string{"PROXY_TRACING_TYPE", "OCIS_TRACING_TYPE"},
|
||||
Destination: &cfg.Tracing.Type,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-endpoint",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the agent",
|
||||
EnvVars: []string{"PROXY_TRACING_ENDPOINT"},
|
||||
EnvVars: []string{"PROXY_TRACING_ENDPOINT", "OCIS_TRACING_ENDPOINT"},
|
||||
Destination: &cfg.Tracing.Endpoint,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "tracing-collector",
|
||||
Value: "",
|
||||
Usage: "Endpoint for the collector",
|
||||
EnvVars: []string{"PROXY_TRACING_COLLECTOR"},
|
||||
EnvVars: []string{"PROXY_TRACING_COLLECTOR", "OCIS_TRACING_COLLECTOR"},
|
||||
Destination: &cfg.Tracing.Collector,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
@@ -291,6 +291,14 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
|
||||
Destination: &cfg.AccountBackend,
|
||||
},
|
||||
|
||||
&cli.StringFlag{
|
||||
Name: "machine-auth-api-key",
|
||||
Value: flags.OverrideDefaultString(cfg.MachineAuthAPIKey, "change-me-please"),
|
||||
Usage: "the API key to be used for the machine auth driver in reva",
|
||||
EnvVars: []string{"PROXY_MACHINE_AUTH_API_KEY", "OCIS_MACHINE_AUTH_API_KEY"},
|
||||
Destination: &cfg.MachineAuthAPIKey,
|
||||
},
|
||||
|
||||
// Reva Middlewares Config
|
||||
&cli.StringSliceFlag{
|
||||
Name: "proxy-user-agent-lock-in",
|
||||
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/middleware"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ func AccessLog(logger log.Logger) func(http.Handler) http.Handler {
|
||||
|
||||
logger.Info().
|
||||
Str("proto", r.Proto).
|
||||
Str("request", r.Header.Get("X-Request-ID")).
|
||||
Str("request", chimiddleware.GetReqID(r.Context())).
|
||||
Str("remote-addr", r.RemoteAddr).
|
||||
Str("method", r.Method).
|
||||
Int("status", wrap.Status()).
|
||||
|
||||
@@ -4,12 +4,9 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/cs3org/reva/pkg/auth/scope"
|
||||
"github.com/owncloud/ocis/proxy/pkg/user/backend"
|
||||
|
||||
revactx "github.com/cs3org/reva/pkg/ctx"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/oidc"
|
||||
)
|
||||
@@ -21,18 +18,9 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl
|
||||
logger := options.Logger
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": options.TokenManagerConfig.JWTSecret,
|
||||
"expires": int64(60),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msg("Could not initialize token-manager")
|
||||
}
|
||||
|
||||
return &accountResolver{
|
||||
next: next,
|
||||
logger: logger,
|
||||
tokenManager: tokenManager,
|
||||
userProvider: options.UserProvider,
|
||||
userOIDCClaim: options.UserOIDCClaim,
|
||||
userCS3Claim: options.UserCS3Claim,
|
||||
@@ -44,7 +32,6 @@ func AccountResolver(optionSetters ...Option) func(next http.Handler) http.Handl
|
||||
type accountResolver struct {
|
||||
next http.Handler
|
||||
logger log.Logger
|
||||
tokenManager token.Manager
|
||||
userProvider backend.UserBackend
|
||||
autoProvisionAccounts bool
|
||||
userOIDCClaim string
|
||||
@@ -56,6 +43,7 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
claims := oidc.FromContext(ctx)
|
||||
u, ok := revactx.ContextGetUser(ctx)
|
||||
token := ""
|
||||
// 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 {
|
||||
@@ -74,7 +62,7 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
u, err = m.userProvider.GetUserByClaims(req.Context(), m.userCS3Claim, value, true)
|
||||
u, token, err = m.userProvider.GetUserByClaims(req.Context(), m.userCS3Claim, value, true)
|
||||
|
||||
if errors.Is(err, backend.ErrAccountNotFound) {
|
||||
m.logger.Debug().Str("claim", m.userOIDCClaim).Str("value", value).Msg("User by claim not found")
|
||||
@@ -108,18 +96,6 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
m.logger.Debug().Interface("claims", claims).Interface("user", u).Msg("associated claims with user")
|
||||
}
|
||||
|
||||
s, err := scope.AddOwnerScope(nil)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("could not get owner scope")
|
||||
return
|
||||
}
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Set(revactx.TokenHeader, token)
|
||||
|
||||
m.next.ServeHTTP(w, req)
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
"testing"
|
||||
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/auth/scope"
|
||||
revactx "github.com/cs3org/reva/pkg/ctx"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/oidc"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
@@ -106,9 +108,20 @@ func TestInternalServerErrorOnMissingMailAndUsername(t *testing.T) {
|
||||
}
|
||||
|
||||
func newMockAccountResolver(userBackendResult *userv1beta1.User, userBackendErr error, oidcclaim, cs3claim string) http.Handler {
|
||||
tokenManager, _ := jwt.New(map[string]interface{}{
|
||||
"secret": "change-me",
|
||||
"expires": int64(60),
|
||||
})
|
||||
|
||||
token := ""
|
||||
if userBackendResult != nil {
|
||||
s, _ := scope.AddOwnerScope(nil)
|
||||
token, _ = tokenManager.MintToken(context.Background(), userBackendResult, s)
|
||||
}
|
||||
|
||||
mock := &test.UserBackendMock{
|
||||
GetUserByClaimsFunc: func(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, error) {
|
||||
return userBackendResult, userBackendErr
|
||||
GetUserByClaimsFunc: func(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, string, error) {
|
||||
return userBackendResult, token, userBackendErr
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ func BasicAuth(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
|
||||
removeSuperfluousAuthenticate(w)
|
||||
login, password, _ := req.BasicAuth()
|
||||
user, err := h.userProvider.Authenticate(req.Context(), login, password)
|
||||
user, _, err := h.userProvider.Authenticate(req.Context(), login, password)
|
||||
|
||||
// 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-
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
revactx "github.com/cs3org/reva/pkg/ctx"
|
||||
"github.com/cs3org/reva/pkg/rgrpc/status"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
@@ -20,17 +18,9 @@ func CreateHome(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
logger := options.Logger
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
tokenManager, err := jwt.New(map[string]interface{}{
|
||||
"secret": options.TokenManagerConfig.JWTSecret,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Fatal().Err(err).Msgf("Could not initialize token-manager")
|
||||
}
|
||||
|
||||
return &createHome{
|
||||
next: next,
|
||||
logger: logger,
|
||||
tokenManager: tokenManager,
|
||||
revaGatewayClient: options.RevaGatewayClient,
|
||||
}
|
||||
}
|
||||
@@ -39,7 +29,6 @@ func CreateHome(optionSetters ...Option) func(next http.Handler) http.Handler {
|
||||
type createHome struct {
|
||||
next http.Handler
|
||||
logger log.Logger
|
||||
tokenManager token.Manager
|
||||
revaGatewayClient gateway.GatewayAPIClient
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ type Options struct {
|
||||
// PreSignedURLConfig to configure the middleware
|
||||
PreSignedURLConfig config.PreSignedURL
|
||||
// UserOIDCClaim to read from the oidc claims
|
||||
UserOIDCClaim string
|
||||
UserOIDCClaim string
|
||||
// UserCS3Claim to use when looking up a user in the CS3 API
|
||||
UserCS3Claim string
|
||||
UserCS3Claim string
|
||||
// AutoprovisionAccounts when an accountResolver does not exist.
|
||||
AutoprovisionAccounts bool
|
||||
// EnableBasicAuth to allow basic auth
|
||||
|
||||
@@ -44,31 +44,25 @@ func (m selectorCookie) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
selectorCookieName = m.policySelector.Claims.SelectorCookieName
|
||||
}
|
||||
|
||||
_, err := req.Cookie(selectorCookieName)
|
||||
if err != nil {
|
||||
// no cookie there - try to add one
|
||||
if oidc.FromContext(req.Context()) != nil {
|
||||
// update cookie
|
||||
if oidc.FromContext(req.Context()) != nil {
|
||||
|
||||
selectorFunc, err := policy.LoadSelector(&m.policySelector)
|
||||
if err != nil {
|
||||
m.logger.Err(err)
|
||||
}
|
||||
|
||||
selector, err := selectorFunc(req)
|
||||
if err != nil {
|
||||
m.logger.Err(err)
|
||||
}
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: selectorCookieName,
|
||||
Value: selector,
|
||||
Domain: req.Host,
|
||||
Path: "/",
|
||||
MaxAge: 60 * 60,
|
||||
HttpOnly: true,
|
||||
}
|
||||
http.SetCookie(w, &cookie)
|
||||
selectorFunc, err := policy.LoadSelector(&m.policySelector)
|
||||
if err != nil {
|
||||
m.logger.Err(err)
|
||||
}
|
||||
|
||||
selector, err := selectorFunc(req)
|
||||
if err != nil {
|
||||
m.logger.Err(err)
|
||||
}
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: selectorCookieName,
|
||||
Value: selector,
|
||||
Path: "/",
|
||||
}
|
||||
http.SetCookie(w, &cookie)
|
||||
}
|
||||
|
||||
m.next.ServeHTTP(w, req)
|
||||
|
||||
@@ -48,7 +48,7 @@ func (m signedURLAuth) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := m.userProvider.GetUserByClaims(req.Context(), "username", req.URL.Query().Get("OC-Credential"), true)
|
||||
user, _, err := m.userProvider.GetUserByClaims(req.Context(), "username", req.URL.Query().Get("OC-Credential"), true)
|
||||
if err != nil {
|
||||
m.logger.Error().Err(err).Msg("Could not get user by claim")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
@@ -164,13 +164,8 @@ func NewMigrationSelector(cfg *config.MigrationSelectorConf, ss accounts.Account
|
||||
// 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(r *http.Request) (s string, err error) {
|
||||
// use cookie first if provided
|
||||
selectorCookie, err := r.Cookie(cfg.SelectorCookieName)
|
||||
if err == nil {
|
||||
return selectorCookie.Value, nil
|
||||
}
|
||||
|
||||
// if no cookie is present, try to route by selector
|
||||
// first, try to route by selector
|
||||
if claims := oidc.FromContext(r.Context()); claims != nil {
|
||||
if p, ok := claims[oidc.OcisRoutingPolicy].(string); ok && p != "" {
|
||||
// TODO check we know the routing policy?
|
||||
@@ -179,6 +174,12 @@ func NewClaimsSelector(cfg *config.ClaimsSelectorConf) Selector {
|
||||
return cfg.DefaultPolicy, nil
|
||||
}
|
||||
|
||||
// use cookie if provided
|
||||
selectorCookie, err := r.Cookie(cfg.SelectorCookieName)
|
||||
if err == nil {
|
||||
return selectorCookie.Value, nil
|
||||
}
|
||||
|
||||
return cfg.UnauthenticatedPolicy, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package policy
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
@@ -129,6 +130,7 @@ func mockAccSvc(retErr bool) proto.AccountsService {
|
||||
type testCase struct {
|
||||
Name string
|
||||
Context context.Context
|
||||
Cookie *http.Cookie
|
||||
Expected string
|
||||
}
|
||||
|
||||
@@ -139,12 +141,17 @@ func TestClaimsSelector(t *testing.T) {
|
||||
})
|
||||
|
||||
var tests = []testCase{
|
||||
{"unatuhenticated", context.Background(), "unauthenticated"},
|
||||
{"default", oidc.NewContext(context.Background(), map[string]interface{}{oidc.OcisRoutingPolicy: ""}), "default"},
|
||||
{"claim-value", oidc.NewContext(context.Background(), map[string]interface{}{oidc.OcisRoutingPolicy: "ocis.routing.policy-value"}), "ocis.routing.policy-value"},
|
||||
{"unauthenticated", context.Background(), nil, "unauthenticated"},
|
||||
{"default", oidc.NewContext(context.Background(), map[string]interface{}{oidc.OcisRoutingPolicy: ""}), nil, "default"},
|
||||
{"claim-value", oidc.NewContext(context.Background(), map[string]interface{}{oidc.OcisRoutingPolicy: "ocis.routing.policy-value"}), nil, "ocis.routing.policy-value"},
|
||||
{"cookie-only", context.Background(), &http.Cookie{Name: SelectorCookieName, Value: "cookie"}, "cookie"},
|
||||
{"claim-can-override-cookie", oidc.NewContext(context.Background(), map[string]interface{}{oidc.OcisRoutingPolicy: "ocis.routing.policy-value"}), &http.Cookie{Name: SelectorCookieName, Value: "cookie"}, "ocis.routing.policy-value"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
r := httptest.NewRequest("GET", "https://example.com", nil)
|
||||
if tc.Cookie != nil {
|
||||
r.AddCookie(tc.Cookie)
|
||||
}
|
||||
nr := r.WithContext(tc.Context)
|
||||
got, err := sel(nr)
|
||||
if err != nil {
|
||||
@@ -172,16 +179,16 @@ func TestRegexSelector(t *testing.T) {
|
||||
})
|
||||
|
||||
var tests = []testCase{
|
||||
{"unauthenticated", context.Background(), "unauthenticated"},
|
||||
{"default", revactx.ContextSetUser(context.Background(), &userv1beta1.User{}), "default"},
|
||||
{"mail-ocis", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Mail: "marie@example.org"}), "ocis"},
|
||||
{"mail-oc10", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Mail: "einstein@example.org"}), "oc10"},
|
||||
{"username-einstein", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "einstein"}), "ocis"},
|
||||
{"username-feynman", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "feynman"}), "ocis"},
|
||||
{"username-marie", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "marie"}), "oc10"},
|
||||
{"id-nil", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{}}), "default"},
|
||||
{"id-1", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "4c510ada-c86b-4815-8820-42cdf82c3d51"}}), "ocis"},
|
||||
{"id-2", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}}), "oc10"},
|
||||
{"unauthenticated", context.Background(), nil, "unauthenticated"},
|
||||
{"default", revactx.ContextSetUser(context.Background(), &userv1beta1.User{}), nil, "default"},
|
||||
{"mail-ocis", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Mail: "marie@example.org"}), nil, "ocis"},
|
||||
{"mail-oc10", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Mail: "einstein@example.org"}), nil, "oc10"},
|
||||
{"username-einstein", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "einstein"}), nil, "ocis"},
|
||||
{"username-feynman", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "feynman"}), nil, "ocis"},
|
||||
{"username-marie", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Username: "marie"}), nil, "oc10"},
|
||||
{"id-nil", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{}}), nil, "default"},
|
||||
{"id-1", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "4c510ada-c86b-4815-8820-42cdf82c3d51"}}), nil, "ocis"},
|
||||
{"id-2", revactx.ContextSetUser(context.Background(), &userv1beta1.User{Id: &userv1beta1.UserId{OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c"}}), nil, "oc10"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
||||
+36
-21
@@ -2,6 +2,7 @@ package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
@@ -10,21 +11,25 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncloud/ocis/proxy/pkg/proxy/policy"
|
||||
"go.opencensus.io/plugin/ochttp/propagation/tracecontext"
|
||||
"go.opencensus.io/trace"
|
||||
chimiddleware "github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
pkgtrace "github.com/owncloud/ocis/ocis-pkg/tracing"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/proxy/pkg/proxy/policy"
|
||||
proxytracing "github.com/owncloud/ocis/proxy/pkg/tracing"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// MultiHostReverseProxy extends httputil to support multiple hosts with diffent policies
|
||||
// MultiHostReverseProxy extends "httputil" to support multiple hosts with different policies
|
||||
type MultiHostReverseProxy struct {
|
||||
httputil.ReverseProxy
|
||||
Directors map[string]map[config.RouteType]map[string]func(req *http.Request)
|
||||
PolicySelector policy.Selector
|
||||
logger log.Logger
|
||||
propagator tracecontext.HTTPFormat
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
@@ -214,17 +219,23 @@ func (p *MultiHostReverseProxy) AddHost(policy string, target *url.URL, rt confi
|
||||
}
|
||||
|
||||
func (p *MultiHostReverseProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
var span *trace.Span
|
||||
var (
|
||||
ctx = r.Context()
|
||||
span trace.Span
|
||||
)
|
||||
|
||||
// Start root span.
|
||||
if p.config.Tracing.Enabled {
|
||||
ctx, span = trace.StartSpan(ctx, r.URL.String())
|
||||
defer span.End()
|
||||
p.propagator.SpanContextToRequest(span.SpanContext(), r)
|
||||
}
|
||||
tracer := proxytracing.TraceProvider.Tracer("proxy")
|
||||
ctx, span = tracer.Start(ctx, fmt.Sprintf("%s %v", r.Method, r.URL.Path))
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(
|
||||
attribute.KeyValue{
|
||||
Key: "x-request-id",
|
||||
Value: attribute.StringValue(chimiddleware.GetReqID(r.Context())),
|
||||
})
|
||||
|
||||
pkgtrace.Propagator.Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||
|
||||
// Call upstream ServeHTTP
|
||||
p.ReverseProxy.ServeHTTP(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
@@ -279,18 +290,22 @@ func defaultPolicies() []config.Policy {
|
||||
Endpoint: "/signin/",
|
||||
Backend: "http://localhost:9130",
|
||||
},
|
||||
{
|
||||
Endpoint: "/archiver",
|
||||
Backend: "http://localhost:9140",
|
||||
},
|
||||
{
|
||||
Type: config.RegexRoute,
|
||||
Endpoint: "/ocs/v[12].php/cloud/(users?|groups)", // we have `user`, `users` and `groups` in ocis-ocs
|
||||
Backend: "http://localhost:9110",
|
||||
},
|
||||
{
|
||||
Endpoint: "/ocs/",
|
||||
Endpoint: "/app/",
|
||||
Backend: "http://localhost:9140",
|
||||
},
|
||||
{
|
||||
Endpoint: "/ocs/v[12].php/cloud/users/signing-key",
|
||||
Backend: "http://localhost:9110",
|
||||
Endpoint: "/ocs/",
|
||||
Backend: "http://localhost:9140",
|
||||
},
|
||||
{
|
||||
Type: config.QueryRoute,
|
||||
@@ -347,10 +362,6 @@ func defaultPolicies() []config.Policy {
|
||||
Endpoint: "/settings.js",
|
||||
Backend: "http://localhost:9190",
|
||||
},
|
||||
{
|
||||
Endpoint: "/onlyoffice.js",
|
||||
Backend: "http://localhost:9220",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -372,6 +383,10 @@ func defaultPolicies() []config.Policy {
|
||||
Endpoint: "/signin/",
|
||||
Backend: "http://localhost:9130",
|
||||
},
|
||||
{
|
||||
Endpoint: "/archiver",
|
||||
Backend: "http://localhost:9140",
|
||||
},
|
||||
{
|
||||
Endpoint: "/ocs/",
|
||||
Backend: "https://demo.owncloud.com",
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/justinas/alice"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/proxy/pkg/metrics"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
|
||||
@@ -1,90 +1,23 @@
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"contrib.go.opencensus.io/exporter/jaeger"
|
||||
"contrib.go.opencensus.io/exporter/ocagent"
|
||||
"contrib.go.opencensus.io/exporter/zipkin"
|
||||
openzipkin "github.com/openzipkin/zipkin-go"
|
||||
zipkinhttp "github.com/openzipkin/zipkin-go/reporter/http"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
pkgtrace "github.com/owncloud/ocis/ocis-pkg/tracing"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"go.opencensus.io/stats/view"
|
||||
"go.opencensus.io/trace"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
func Configure(cfg *config.Config, logger log.Logger) error {
|
||||
var (
|
||||
// TraceProvider is the global trace provider for the proxy service.
|
||||
TraceProvider = trace.NewNoopTracerProvider()
|
||||
)
|
||||
|
||||
func Configure(cfg *config.Config) error {
|
||||
var err error
|
||||
if cfg.Tracing.Enabled {
|
||||
switch t := cfg.Tracing.Type; t {
|
||||
case "agent":
|
||||
exporter, err := ocagent.NewExporter(
|
||||
ocagent.WithReconnectionPeriod(5*time.Second),
|
||||
ocagent.WithAddress(cfg.Tracing.Endpoint),
|
||||
ocagent.WithServiceName(cfg.Tracing.Service),
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create agent tracing")
|
||||
return err
|
||||
}
|
||||
trace.RegisterExporter(exporter)
|
||||
view.RegisterExporter(exporter)
|
||||
case "jaeger":
|
||||
exporter, err := jaeger.NewExporter(
|
||||
jaeger.Options{
|
||||
AgentEndpoint: cfg.Tracing.Endpoint,
|
||||
CollectorEndpoint: cfg.Tracing.Collector,
|
||||
Process: jaeger.Process{
|
||||
ServiceName: cfg.Tracing.Service,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create jaeger tracing")
|
||||
return err
|
||||
}
|
||||
trace.RegisterExporter(exporter)
|
||||
case "zipkin":
|
||||
endpoint, err := openzipkin.NewEndpoint(
|
||||
cfg.Tracing.Service,
|
||||
cfg.Tracing.Endpoint,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("endpoint", cfg.Tracing.Endpoint).
|
||||
Str("collector", cfg.Tracing.Collector).
|
||||
Msg("Failed to create zipkin tracing")
|
||||
return err
|
||||
}
|
||||
exporter := zipkin.NewExporter(
|
||||
zipkinhttp.NewReporter(
|
||||
cfg.Tracing.Collector,
|
||||
),
|
||||
endpoint,
|
||||
)
|
||||
trace.RegisterExporter(exporter)
|
||||
default:
|
||||
logger.Warn().
|
||||
Str("type", t).
|
||||
Msg("Unknown tracing backend")
|
||||
if TraceProvider, err = pkgtrace.GetTraceProvider(cfg.Tracing.Endpoint, cfg.Tracing.Collector, "proxy", cfg.Tracing.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
trace.ApplyConfig(
|
||||
trace.Config{
|
||||
DefaultSampler: trace.AlwaysSample(),
|
||||
},
|
||||
)
|
||||
} else {
|
||||
logger.Debug().
|
||||
Msg("Tracing is not enabled")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/auth/scope"
|
||||
"github.com/cs3org/reva/pkg/token"
|
||||
accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/oidc"
|
||||
@@ -15,11 +17,12 @@ import (
|
||||
)
|
||||
|
||||
// NewAccountsServiceUserBackend creates a user-provider which fetches users from the ocis accounts-service
|
||||
func NewAccountsServiceUserBackend(ac accounts.AccountsService, rs settings.RoleService, oidcISS string, logger log.Logger) UserBackend {
|
||||
func NewAccountsServiceUserBackend(ac accounts.AccountsService, rs settings.RoleService, oidcISS string, tokenManager token.Manager, logger log.Logger) UserBackend {
|
||||
return &accountsServiceBackend{
|
||||
accountsClient: ac,
|
||||
settingsRoleService: rs,
|
||||
OIDCIss: oidcISS,
|
||||
tokenManager: tokenManager,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
@@ -29,9 +32,10 @@ type accountsServiceBackend struct {
|
||||
settingsRoleService settings.RoleService
|
||||
OIDCIss string
|
||||
logger log.Logger
|
||||
tokenManager token.Manager
|
||||
}
|
||||
|
||||
func (a accountsServiceBackend) GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, error) {
|
||||
func (a accountsServiceBackend) GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, string, error) {
|
||||
var account *accounts.Account
|
||||
var status int
|
||||
var query string
|
||||
@@ -44,38 +48,41 @@ func (a accountsServiceBackend) GetUserByClaims(ctx context.Context, claim, valu
|
||||
case "id":
|
||||
query = fmt.Sprintf("id eq '%s'", strings.ReplaceAll(value, "'", "''"))
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid user by claim lookup must be 'mail', 'username' or 'id")
|
||||
return nil, "", fmt.Errorf("invalid user by claim lookup must be 'mail', 'username' or 'id")
|
||||
}
|
||||
|
||||
account, status = a.getAccount(ctx, query)
|
||||
if status == http.StatusNotFound {
|
||||
return nil, ErrAccountNotFound
|
||||
return nil, "", ErrAccountNotFound
|
||||
}
|
||||
|
||||
if status != 0 || account == nil {
|
||||
return nil, fmt.Errorf("could not get account, got status: %d", status)
|
||||
return nil, "", fmt.Errorf("could not get account, got status: %d", status)
|
||||
}
|
||||
|
||||
if !account.AccountEnabled {
|
||||
return nil, ErrAccountDisabled
|
||||
return nil, "", ErrAccountDisabled
|
||||
}
|
||||
|
||||
user := a.accountToUser(account)
|
||||
|
||||
if !withRoles {
|
||||
return user, nil
|
||||
if withRoles {
|
||||
if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
|
||||
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
|
||||
}
|
||||
}
|
||||
|
||||
if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
|
||||
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
|
||||
token, err := a.generateToken(ctx, user)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
return user, token, nil
|
||||
|
||||
}
|
||||
|
||||
// Authenticate authenticates against the accounts services and returns the user on success
|
||||
func (a *accountsServiceBackend) Authenticate(ctx context.Context, username string, password string) (*cs3.User, error) {
|
||||
func (a *accountsServiceBackend) Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error) {
|
||||
query := fmt.Sprintf(
|
||||
"login eq '%s' and password eq '%s'",
|
||||
strings.ReplaceAll(username, "'", "''"),
|
||||
@@ -84,23 +91,28 @@ func (a *accountsServiceBackend) Authenticate(ctx context.Context, username stri
|
||||
account, status := a.getAccount(ctx, query)
|
||||
|
||||
if status != 0 {
|
||||
return nil, fmt.Errorf("could not authenticate with username, password for user %s. Status: %d", username, status)
|
||||
return nil, "", fmt.Errorf("could not authenticate with username, password for user %s. Status: %d", username, status)
|
||||
}
|
||||
|
||||
user := a.accountToUser(account)
|
||||
|
||||
token, err := a.generateToken(ctx, user)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if err := injectRoles(ctx, user, a.settingsRoleService); err != nil {
|
||||
a.logger.Warn().Err(err).Msgf("Could not load roles... continuing without")
|
||||
}
|
||||
|
||||
return user, nil
|
||||
return user, token, nil
|
||||
}
|
||||
|
||||
func (a accountsServiceBackend) CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error) {
|
||||
req := &accounts.CreateAccountRequest{
|
||||
Account: &accounts.Account{
|
||||
CreationType: "LocalAccount",
|
||||
AccountEnabled: true,
|
||||
CreationType: "LocalAccount",
|
||||
AccountEnabled: true,
|
||||
},
|
||||
}
|
||||
var ok bool
|
||||
@@ -144,7 +156,7 @@ func (a *accountsServiceBackend) accountToUser(account *accounts.Account) *cs3.U
|
||||
Id: &cs3.UserId{
|
||||
OpaqueId: account.Id,
|
||||
Idp: a.OIDCIss,
|
||||
Type: cs3.UserType_USER_TYPE_PRIMARY, // TODO: once we have support for other user types, this needs to be inferred
|
||||
Type: cs3.UserType_USER_TYPE_PRIMARY, // TODO: once we have support for other user types, this needs to be inferred
|
||||
},
|
||||
Username: account.OnPremisesSamAccountName,
|
||||
DisplayName: account.DisplayName,
|
||||
@@ -185,6 +197,21 @@ func (a *accountsServiceBackend) getAccount(ctx context.Context, query string) (
|
||||
return
|
||||
}
|
||||
|
||||
func (a *accountsServiceBackend) generateToken(ctx context.Context, u *cs3.User) (string, error) {
|
||||
s, err := scope.AddOwnerScope(nil)
|
||||
if err != nil {
|
||||
a.logger.Error().Err(err).Msg("could not get owner scope")
|
||||
return "", err
|
||||
}
|
||||
|
||||
token, err := a.tokenManager.MintToken(ctx, u, s)
|
||||
if err != nil {
|
||||
a.logger.Error().Err(err).Msg("could not mint token")
|
||||
return "", err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func expandGroups(account *accounts.Account) []string {
|
||||
groups := make([]string, len(account.MemberOf))
|
||||
for i := range account.MemberOf {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/asim/go-micro/v3/client"
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/cs3org/reva/pkg/token/manager/jwt"
|
||||
accounts "github.com/owncloud/ocis/accounts/pkg/proto/v0"
|
||||
"github.com/owncloud/ocis/ocis-pkg/log"
|
||||
"github.com/owncloud/ocis/ocis-pkg/oidc"
|
||||
@@ -51,7 +52,7 @@ func TestGetUserByClaimsFound(t *testing.T) {
|
||||
|
||||
for k := range tests {
|
||||
t.Run(tests[k].id, func(t *testing.T) {
|
||||
u, err := accBackend.GetUserByClaims(context.Background(), tests[k].claim, tests[k].value, true)
|
||||
u, _, err := accBackend.GetUserByClaims(context.Background(), tests[k].claim, tests[k].value, true)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, u)
|
||||
@@ -64,7 +65,7 @@ func TestGetUserByClaimsFound(t *testing.T) {
|
||||
func TestGetUserByClaimsNotFound(t *testing.T) {
|
||||
accBackend := newAccountsBackend([]*accounts.Account{}, expectedRoles)
|
||||
|
||||
u, err := accBackend.GetUserByClaims(context.Background(), "mail", "foo@example.com", true)
|
||||
u, _, err := accBackend.GetUserByClaims(context.Background(), "mail", "foo@example.com", true)
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, u)
|
||||
@@ -73,7 +74,7 @@ func TestGetUserByClaimsNotFound(t *testing.T) {
|
||||
|
||||
func TestGetUserByClaimsInvalidClaim(t *testing.T) {
|
||||
accBackend := newAccountsBackend([]*accounts.Account{}, expectedRoles)
|
||||
u, err := accBackend.GetUserByClaims(context.Background(), "invalidClaimName", "efwfwfwfe", true)
|
||||
u, _, err := accBackend.GetUserByClaims(context.Background(), "invalidClaimName", "efwfwfwfe", true)
|
||||
|
||||
assert.Nil(t, u)
|
||||
assert.Error(t, err)
|
||||
@@ -81,7 +82,7 @@ func TestGetUserByClaimsInvalidClaim(t *testing.T) {
|
||||
|
||||
func TestGetUserByClaimsDisabledAccount(t *testing.T) {
|
||||
accBackend := newAccountsBackend([]*accounts.Account{{AccountEnabled: false}}, expectedRoles)
|
||||
u, err := accBackend.GetUserByClaims(context.Background(), "mail", "foo@example.com", true)
|
||||
u, _, err := accBackend.GetUserByClaims(context.Background(), "mail", "foo@example.com", true)
|
||||
|
||||
assert.Nil(t, u)
|
||||
assert.Error(t, err)
|
||||
@@ -90,7 +91,7 @@ func TestGetUserByClaimsDisabledAccount(t *testing.T) {
|
||||
|
||||
func TestAuthenticate(t *testing.T) {
|
||||
accBackend := newAccountsBackend(mockAccResp, expectedRoles)
|
||||
u, err := accBackend.Authenticate(context.Background(), "foo", "secret")
|
||||
u, _, err := accBackend.Authenticate(context.Background(), "foo", "secret")
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, u)
|
||||
@@ -99,7 +100,7 @@ func TestAuthenticate(t *testing.T) {
|
||||
|
||||
func TestAuthenticateFailed(t *testing.T) {
|
||||
accBackend := newAccountsBackend([]*accounts.Account{}, expectedRoles)
|
||||
u, err := accBackend.Authenticate(context.Background(), "foo", "secret")
|
||||
u, _, err := accBackend.Authenticate(context.Background(), "foo", "secret")
|
||||
|
||||
assert.Nil(t, u)
|
||||
assert.Error(t, err)
|
||||
@@ -151,7 +152,11 @@ func assertUserMatchesAccount(t *testing.T, exp *accounts.Account, act *userv1be
|
||||
|
||||
func newAccountsBackend(mockAccounts []*accounts.Account, mockRoles []*settings.UserRoleAssignment) UserBackend {
|
||||
accSvc, roleSvc := getAccountService(mockAccounts, nil), getRoleService(mockRoles, nil)
|
||||
accBackend := NewAccountsServiceUserBackend(accSvc, roleSvc, "https://idp.example.org", log.NewLogger())
|
||||
tokenManager, _ := jwt.New(map[string]interface{}{
|
||||
"secret": "change-me",
|
||||
"expires": int64(24 * 60 * 60),
|
||||
})
|
||||
accBackend := NewAccountsServiceUserBackend(accSvc, roleSvc, "https://idp.example.org", tokenManager, log.NewLogger())
|
||||
zerolog.SetGlobalLevel(zerolog.Disabled)
|
||||
return accBackend
|
||||
}
|
||||
@@ -169,10 +174,10 @@ func getAccountService(expectedResponse []*accounts.Account, err error) *account
|
||||
}
|
||||
}
|
||||
|
||||
func getRoleService(expectedRespone []*settings.UserRoleAssignment, err error) *settings.MockRoleService {
|
||||
func getRoleService(expectedResponse []*settings.UserRoleAssignment, err error) *settings.MockRoleService {
|
||||
return &settings.MockRoleService{
|
||||
ListRoleAssignmentsFunc: func(ctx context.Context, req *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) (*settings.ListRoleAssignmentsResponse, error) {
|
||||
return &settings.ListRoleAssignmentsResponse{Assignments: expectedRespone}, err
|
||||
return &settings.ListRoleAssignmentsResponse{Assignments: expectedResponse}, err
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ var (
|
||||
|
||||
// UserBackend allows the proxy to retrieve users from different user-backends (accounts-service, CS3)
|
||||
type UserBackend interface {
|
||||
GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, error)
|
||||
Authenticate(ctx context.Context, username string, password string) (*cs3.User, error)
|
||||
GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, string, error)
|
||||
Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error)
|
||||
CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error)
|
||||
GetUserGroups(ctx context.Context, userID string)
|
||||
}
|
||||
|
||||
@@ -14,42 +14,48 @@ import (
|
||||
)
|
||||
|
||||
type cs3backend struct {
|
||||
userProvider cs3.UserAPIClient
|
||||
settingsRoleService settings.RoleService
|
||||
authProvider RevaAuthenticator
|
||||
machineAuthAPIKey string
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// NewCS3UserBackend creates a user-provider which fetches users from a CS3 UserBackend
|
||||
func NewCS3UserBackend(up cs3.UserAPIClient, rs settings.RoleService, ap RevaAuthenticator, logger log.Logger) UserBackend {
|
||||
func NewCS3UserBackend(rs settings.RoleService, ap RevaAuthenticator, machineAuthAPIKey string, logger log.Logger) UserBackend {
|
||||
return &cs3backend{
|
||||
userProvider: up,
|
||||
settingsRoleService: rs,
|
||||
authProvider: ap,
|
||||
machineAuthAPIKey: machineAuthAPIKey,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, error) {
|
||||
res, err := c.userProvider.GetUserByClaim(ctx, &cs3.GetUserByClaimRequest{
|
||||
Claim: claim,
|
||||
Value: value,
|
||||
func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string, withRoles bool) (*cs3.User, string, error) {
|
||||
// We only support authentication via username for now
|
||||
if claim != "username" {
|
||||
return nil, "", fmt.Errorf("claim: %s not supported", claim)
|
||||
}
|
||||
|
||||
res, err := c.authProvider.Authenticate(ctx, &gateway.AuthenticateRequest{
|
||||
Type: "machine",
|
||||
ClientId: value,
|
||||
ClientSecret: c.machineAuthAPIKey,
|
||||
})
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("could not get user by claim %v with value %v: %w", claim, value, err)
|
||||
return nil, "", fmt.Errorf("could not get user by claim %v with value %v: %w", claim, value, err)
|
||||
case res.Status.Code != rpcv1beta1.Code_CODE_OK:
|
||||
if res.Status.Code == rpcv1beta1.Code_CODE_NOT_FOUND {
|
||||
return nil, ErrAccountNotFound
|
||||
return nil, "", ErrAccountNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("could not get user by claim %v with value %v : %w ", claim, value, err)
|
||||
return nil, "", fmt.Errorf("could not get user by claim %v with value %v : %w ", claim, value, err)
|
||||
}
|
||||
|
||||
user := res.User
|
||||
|
||||
if !withRoles {
|
||||
return user, nil
|
||||
return user, res.Token, nil
|
||||
}
|
||||
|
||||
var roleIDs []string
|
||||
@@ -82,10 +88,10 @@ func (c *cs3backend) GetUserByClaims(ctx context.Context, claim, value string, w
|
||||
user.Opaque.Map["roles"] = enc
|
||||
}
|
||||
|
||||
return res.User, nil
|
||||
return user, res.Token, nil
|
||||
}
|
||||
|
||||
func (c *cs3backend) Authenticate(ctx context.Context, username string, password string) (*cs3.User, error) {
|
||||
func (c *cs3backend) Authenticate(ctx context.Context, username string, password string) (*cs3.User, string, error) {
|
||||
res, err := c.authProvider.Authenticate(ctx, &gateway.AuthenticateRequest{
|
||||
Type: "basic",
|
||||
ClientId: username,
|
||||
@@ -94,12 +100,12 @@ func (c *cs3backend) Authenticate(ctx context.Context, username string, password
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, fmt.Errorf("could not authenticate with username and password user: %s, %w", username, err)
|
||||
return nil, "", fmt.Errorf("could not authenticate with username and password user: %s, %w", username, err)
|
||||
case res.Status.Code != rpcv1beta1.Code_CODE_OK:
|
||||
return nil, fmt.Errorf("could not authenticate with username and password user: %s, got code: %d", username, res.Status.Code)
|
||||
return nil, "", fmt.Errorf("could not authenticate with username and password user: %s, got code: %d", username, res.Status.Code)
|
||||
}
|
||||
|
||||
return res.User, nil
|
||||
return res.User, res.Token, nil
|
||||
}
|
||||
|
||||
func (c *cs3backend) CreateUserFromClaims(ctx context.Context, claims map[string]interface{}) (*cs3.User, error) {
|
||||
|
||||
@@ -5,9 +5,10 @@ package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/ocis/proxy/pkg/user/backend"
|
||||
"sync"
|
||||
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/owncloud/ocis/proxy/pkg/user/backend"
|
||||
)
|
||||
|
||||
// Ensure, that UserBackendMock does implement UserBackend.
|
||||
@@ -40,13 +41,13 @@ var _ backend.UserBackend = &UserBackendMock{}
|
||||
// }
|
||||
type UserBackendMock struct {
|
||||
// AuthenticateFunc mocks the Authenticate method.
|
||||
AuthenticateFunc func(ctx context.Context, username string, password string) (*userv1beta1.User, error)
|
||||
AuthenticateFunc func(ctx context.Context, username string, password string) (*userv1beta1.User, string, error)
|
||||
|
||||
// CreateUserFromClaimsFunc mocks the CreateUserFromClaims method.
|
||||
CreateUserFromClaimsFunc func(ctx context.Context, claims map[string]interface{}) (*userv1beta1.User, error)
|
||||
|
||||
// GetUserByClaimsFunc mocks the GetUserByClaims method.
|
||||
GetUserByClaimsFunc func(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, error)
|
||||
GetUserByClaimsFunc func(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, string, error)
|
||||
|
||||
// GetUserGroupsFunc mocks the GetUserGroups method.
|
||||
GetUserGroupsFunc func(ctx context.Context, userID string)
|
||||
@@ -95,7 +96,7 @@ type UserBackendMock struct {
|
||||
}
|
||||
|
||||
// Authenticate calls AuthenticateFunc.
|
||||
func (mock *UserBackendMock) Authenticate(ctx context.Context, username string, password string) (*userv1beta1.User, error) {
|
||||
func (mock *UserBackendMock) Authenticate(ctx context.Context, username string, password string) (*userv1beta1.User, string, error) {
|
||||
if mock.AuthenticateFunc == nil {
|
||||
panic("UserBackendMock.AuthenticateFunc: method is nil but UserBackend.Authenticate was just called")
|
||||
}
|
||||
@@ -169,7 +170,7 @@ func (mock *UserBackendMock) CreateUserFromClaimsCalls() []struct {
|
||||
}
|
||||
|
||||
// GetUserByClaims calls GetUserByClaimsFunc.
|
||||
func (mock *UserBackendMock) GetUserByClaims(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, error) {
|
||||
func (mock *UserBackendMock) GetUserByClaims(ctx context.Context, claim string, value string, withRoles bool) (*userv1beta1.User, string, error) {
|
||||
if mock.GetUserByClaimsFunc == nil {
|
||||
panic("UserBackendMock.GetUserByClaimsFunc: method is nil but UserBackend.GetUserByClaims was just called")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user