mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-05 11:38:50 -06:00
[full-ci] revaBump-v2.40.1 (#1927)
* revaBump-v2.40.0 * adapt tests * bring-#442 * adapt tests * bring-#444 * ocm fixes Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * adapt tests * adapt unit tests Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * revaUpdate-2.40.1 * update opencloud-version-4.0.0-rc.3 --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> Co-authored-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
8
go.mod
8
go.mod
@@ -64,7 +64,7 @@ require (
|
||||
github.com/open-policy-agent/opa v1.10.1
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
|
||||
github.com/opencloud-eu/reva/v2 v2.39.3
|
||||
github.com/opencloud-eu/reva/v2 v2.40.1
|
||||
github.com/opensearch-project/opensearch-go/v4 v4.5.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -372,9 +372,9 @@ require (
|
||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||
github.com/yashtewari/glob-intersection v0.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.6.5 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.6.5 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.6.6 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.6 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.6.6 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -963,8 +963,8 @@ github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIft
|
||||
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
|
||||
github.com/opencloud-eu/reva/v2 v2.39.3 h1:/9NW08Bpy1GaNAPo8HrlyT21Flj8uNnOUyWLud1ehGc=
|
||||
github.com/opencloud-eu/reva/v2 v2.39.3/go.mod h1:kkGiMeEVR59VjDsmWIczWqRcwK8cy9ogTd/u802U3NI=
|
||||
github.com/opencloud-eu/reva/v2 v2.40.1 h1:QwMkbGMhwDSwfk2WxbnTpIig2BugPBaVFjWcy2DSU3U=
|
||||
github.com/opencloud-eu/reva/v2 v2.40.1/go.mod h1:DGH08n2mvtsQLkt8o15FV6m51FwSJJGhjR8Ty+iIJww=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
@@ -1275,12 +1275,12 @@ github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQ
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
|
||||
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
|
||||
go.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA=
|
||||
go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk=
|
||||
go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U=
|
||||
go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo=
|
||||
go.etcd.io/etcd/api/v3 v3.6.6 h1:mcaMp3+7JawWv69p6QShYWS8cIWUOl32bFLb6qf8pOQ=
|
||||
go.etcd.io/etcd/api/v3 v3.6.6/go.mod h1:f/om26iXl2wSkcTA1zGQv8reJRSLVdoEBsi4JdfMrx4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.6 h1:uoqgzSOv2H9KlIF5O1Lsd8sW+eMLuV6wzE3q5GJGQNs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.6.6/go.mod h1:YngfUVmvsvOJ2rRgStIyHsKtOt9SZI2aBJrZiWJhCbI=
|
||||
go.etcd.io/etcd/client/v3 v3.6.6 h1:G5z1wMf5B9SNexoxOHUGBaULurOZPIgGPsW6CN492ec=
|
||||
go.etcd.io/etcd/client/v3 v3.6.6/go.mod h1:36Qv6baQ07znPR3+n7t+Rk5VHEzVYPvFfGmfF4wBHV8=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
|
||||
@@ -16,7 +16,7 @@ var (
|
||||
// LatestTag is the latest released version plus the dev meta version.
|
||||
// Will be overwritten by the release pipeline
|
||||
// Needs a manual change for every tagged release
|
||||
LatestTag = "4.0.0-rc.2+dev"
|
||||
LatestTag = "4.0.0-rc.3+dev"
|
||||
|
||||
// Date indicates the build date.
|
||||
// This has been removed, it looks like you can only replace static strings with recent go versions
|
||||
|
||||
@@ -133,6 +133,10 @@ func CreateUserModelFromCS3(u *cs3user.User) *libregraph.User {
|
||||
OnPremisesSamAccountName: u.GetUsername(),
|
||||
Id: &u.GetId().OpaqueId,
|
||||
}
|
||||
if u.GetId().GetType() == cs3user.UserType_USER_TYPE_FEDERATED {
|
||||
ocmUserId := u.GetId().GetOpaqueId() + "@" + u.GetId().GetIdp()
|
||||
user.Id = &ocmUserId
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
|
||||
24
services/graph/pkg/identity/cache/cache.go
vendored
24
services/graph/pkg/identity/cache/cache.go
vendored
@@ -2,6 +2,8 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
@@ -133,6 +135,20 @@ func (cache IdentityCache) GetAcceptedUser(ctx context.Context, userid string) (
|
||||
return *identity.CreateUserModelFromCS3(u), nil
|
||||
}
|
||||
|
||||
func getIDAndMeshProvider(user string) (id, provider string, err error) {
|
||||
last := strings.LastIndex(user, "@")
|
||||
if last == -1 {
|
||||
return "", "", errors.New("not in the form <id>@<provider>")
|
||||
}
|
||||
if len(user[:last]) == 0 {
|
||||
return "", "", errors.New("empty id")
|
||||
}
|
||||
if len(user[last+1:]) == 0 {
|
||||
return "", "", errors.New("empty provider")
|
||||
}
|
||||
return user[:last], user[last+1:], nil
|
||||
}
|
||||
|
||||
func (cache IdentityCache) GetAcceptedCS3User(ctx context.Context, userid string) (*cs3User.User, error) {
|
||||
var user *cs3user.User
|
||||
if item := cache.users.Get(userid); item == nil {
|
||||
@@ -140,8 +156,14 @@ func (cache IdentityCache) GetAcceptedCS3User(ctx context.Context, userid string
|
||||
if err != nil {
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
id, provider, err := getIDAndMeshProvider(userid)
|
||||
if err != nil {
|
||||
return nil, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
cs3UserID := &cs3User.UserId{
|
||||
OpaqueId: userid,
|
||||
Idp: provider,
|
||||
OpaqueId: id,
|
||||
Type: cs3User.UserType_USER_TYPE_FEDERATED,
|
||||
}
|
||||
user, err = revautils.GetAcceptedUserWithContext(ctx, cs3UserID, gatewayClient)
|
||||
if err != nil {
|
||||
|
||||
@@ -1354,6 +1354,7 @@ var _ = Describe("Users", func() {
|
||||
Username: "federated",
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "federated",
|
||||
Idp: "provider",
|
||||
Type: userv1beta1.UserType_USER_TYPE_FEDERATED,
|
||||
},
|
||||
},
|
||||
@@ -1377,7 +1378,7 @@ var _ = Describe("Users", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(res.Value)).To(Equal(1))
|
||||
Expect(res.Value[0].GetId()).To(Equal("federated"))
|
||||
Expect(res.Value[0].GetId()).To(Equal("federated@provider"))
|
||||
Expect(res.Value[0].GetUserType()).To(Equal("Federated"))
|
||||
})
|
||||
It("does not list federated users when filtering for 'Member' users", func() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@@ -106,7 +107,8 @@ func userIdToIdentity(ctx context.Context, cache cache.IdentityCache, tennantId,
|
||||
|
||||
// federatedIdToIdentity looks the user for the supplied id using the cache and returns it
|
||||
// as a libregraph.Identity
|
||||
func federatedIdToIdentity(ctx context.Context, cache cache.IdentityCache, userID string) (libregraph.Identity, error) {
|
||||
func federatedIdToIdentity(ctx context.Context, cache cache.IdentityCache, cs3UserID *cs3User.UserId) (libregraph.Identity, error) {
|
||||
userID := fmt.Sprintf("%s@%s", cs3UserID.GetOpaqueId(), cs3UserID.GetIdp())
|
||||
identity := libregraph.Identity{
|
||||
Id: libregraph.PtrString(userID),
|
||||
LibreGraphUserType: libregraph.PtrString("Federated"),
|
||||
@@ -123,7 +125,7 @@ func federatedIdToIdentity(ctx context.Context, cache cache.IdentityCache, userI
|
||||
// as a libregraph.Identity. Skips the user lookup if the id type is USER_TYPE_SPACE_OWNER
|
||||
func cs3UserIdToIdentity(ctx context.Context, cache cache.IdentityCache, cs3UserID *cs3User.UserId) (libregraph.Identity, error) {
|
||||
if cs3UserID.GetType() == cs3User.UserType_USER_TYPE_FEDERATED {
|
||||
return federatedIdToIdentity(ctx, cache, cs3UserID.GetOpaqueId())
|
||||
return federatedIdToIdentity(ctx, cache, cs3UserID)
|
||||
}
|
||||
if cs3UserID.GetType() != cs3User.UserType_USER_TYPE_SPACE_OWNER {
|
||||
return userIdToIdentity(ctx, cache, cs3UserID.GetTenantId(), cs3UserID.GetOpaqueId())
|
||||
|
||||
@@ -120,13 +120,12 @@ class GraphHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Federated users have a base64 encoded string of {remoteid}@{provider} as their id
|
||||
* This regex matches only non empty base64 encoded strings
|
||||
* Federated users have a string of {userid}@{provider} as their id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getFederatedUserRegex(): string {
|
||||
return '(?=(.{4})*$)[A-Za-z0-9+/]*={0,2}$';
|
||||
return '^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}@https?://.+$';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2440,14 +2440,6 @@ class FeatureContext extends BehatVariablesContext {
|
||||
],
|
||||
"parameter" => []
|
||||
],
|
||||
[
|
||||
"code" => "%identities_issuer_id_pattern%",
|
||||
"function" => [
|
||||
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
||||
"getFederatedUserRegex"
|
||||
],
|
||||
"parameter" => []
|
||||
],
|
||||
[
|
||||
"code" => "%uuidv4_pattern%",
|
||||
"function" => [
|
||||
|
||||
@@ -327,12 +327,11 @@ class SharingNgContext implements Context {
|
||||
if ($shareType === "user") {
|
||||
$shareeId = $this->featureContext->getAttributeOfCreatedUser($sharee, 'id');
|
||||
if ($federatedShare) {
|
||||
$shareeId = (
|
||||
$this->featureContext->ocmContext->getAcceptedUserByName(
|
||||
$user,
|
||||
$sharee
|
||||
)
|
||||
)['user_id'];
|
||||
$federatedUser = $this->featureContext->ocmContext->getAcceptedUserByName(
|
||||
$user,
|
||||
$sharee
|
||||
);
|
||||
$shareeId = $federatedUser['user_id'] . "@" . $federatedUser['idp'];
|
||||
}
|
||||
} elseif ($shareType === "group") {
|
||||
$shareeId = $this->featureContext->getAttributeOfCreatedGroup($sharee, 'id');
|
||||
@@ -403,7 +402,11 @@ class SharingNgContext implements Context {
|
||||
if ($shareType === "user") {
|
||||
$shareeId = $this->featureContext->getAttributeOfCreatedUser($sharee, 'id');
|
||||
if ($federatedShare) {
|
||||
$shareeId = ($this->featureContext->ocmContext->getAcceptedUserByName($user, $sharee))['user_id'];
|
||||
$federatedUser = $this->featureContext->ocmContext->getAcceptedUserByName(
|
||||
$user,
|
||||
$sharee
|
||||
);
|
||||
$shareeId = $federatedUser['user_id'] . "@" . $federatedUser['idp'];
|
||||
}
|
||||
} elseif ($shareType === "group") {
|
||||
$shareeId = $this->featureContext->getAttributeOfCreatedGroup($sharee, 'id');
|
||||
|
||||
@@ -26,7 +26,7 @@ Feature: ocm well-known URI
|
||||
"const": true
|
||||
},
|
||||
"apiVersion": {
|
||||
"const": "1.1.0"
|
||||
"const": "1.2.0"
|
||||
},
|
||||
"endPoint": {
|
||||
"pattern": "^%base_url%/ocm"
|
||||
@@ -73,10 +73,24 @@ Feature: ocm well-known URI
|
||||
},
|
||||
"capabilities": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 1,
|
||||
"minItems": 4,
|
||||
"maxItems": 4,
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"const": "/invite-accepted"
|
||||
"oneOf": [
|
||||
{
|
||||
"const": "invites"
|
||||
},
|
||||
{
|
||||
"const": "webdav-uri"
|
||||
},
|
||||
{
|
||||
"const": "protocol-object"
|
||||
},
|
||||
{
|
||||
"const": "invite-wayf"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%federated_user_id_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,7 +130,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%federated_user_id_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%federated_user_id_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%federated_user_id_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -484,7 +484,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%uuidv4_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -549,7 +549,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%uuidv4_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -696,7 +696,7 @@ Feature: search federation users
|
||||
},
|
||||
"issuerAssignedId": {
|
||||
"type": "string",
|
||||
"pattern": "^%identities_issuer_id_pattern%$"
|
||||
"pattern": "^%uuidv4_pattern%$"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/github.com/opencloud-eu/reva/v2/cmd/revad/runtime/runtime.go
generated
vendored
33
vendor/github.com/opencloud-eu/reva/v2/cmd/revad/runtime/runtime.go
generated
vendored
@@ -62,16 +62,9 @@ func RunWithOptions(mainConf map[string]interface{}, pidFile string, opts ...Opt
|
||||
|
||||
type coreConf struct {
|
||||
MaxCPUs string `mapstructure:"max_cpus"`
|
||||
TracingEnabled bool `mapstructure:"tracing_enabled"`
|
||||
TracingInsecure bool `mapstructure:"tracing_insecure"`
|
||||
TracingExporter string `mapstructure:"tracing_exporter"`
|
||||
TracingEndpoint string `mapstructure:"tracing_endpoint"`
|
||||
TracingCollector string `mapstructure:"tracing_collector"`
|
||||
TracesExporter string `mapstructure:"traces_exporter"`
|
||||
TracingServiceName string `mapstructure:"tracing_service_name"`
|
||||
|
||||
// TracingService specifies the service. i.e OpenCensus, OpenTelemetry, OpenTracing...
|
||||
TracingService string `mapstructure:"tracing_service"`
|
||||
|
||||
GracefulShutdownTimeout int `mapstructure:"graceful_shutdown_timeout"`
|
||||
}
|
||||
|
||||
@@ -149,20 +142,8 @@ func initServers(mainConf map[string]interface{}, log *zerolog.Logger, tp trace.
|
||||
}
|
||||
|
||||
func initTracing(conf *coreConf) trace.TracerProvider {
|
||||
if conf.TracingEnabled {
|
||||
opts := []rtrace.Option{
|
||||
rtrace.WithExporter(conf.TracingExporter),
|
||||
rtrace.WithEndpoint(conf.TracingEndpoint),
|
||||
rtrace.WithCollector(conf.TracingCollector),
|
||||
rtrace.WithServiceName(conf.TracingServiceName),
|
||||
}
|
||||
if conf.TracingEnabled {
|
||||
opts = append(opts, rtrace.WithEnabled())
|
||||
}
|
||||
if conf.TracingInsecure {
|
||||
opts = append(opts, rtrace.WithInsecure())
|
||||
}
|
||||
tp := rtrace.NewTracerProvider(opts...)
|
||||
if conf.TracesExporter != "none" && conf.TracesExporter != "" {
|
||||
tp := rtrace.NewTracerProvider(conf.TracingServiceName, conf.TracesExporter)
|
||||
rtrace.SetDefaultTracerProvider(tp)
|
||||
return tp
|
||||
}
|
||||
@@ -281,11 +262,9 @@ func parseCoreConfOrDie(v interface{}) *coreConf {
|
||||
|
||||
// tracing defaults to enabled if not explicitly configured
|
||||
if v == nil {
|
||||
c.TracingEnabled = true
|
||||
c.TracingEndpoint = "localhost:6831"
|
||||
} else if _, ok := v.(map[string]interface{})["tracing_enabled"]; !ok {
|
||||
c.TracingEnabled = true
|
||||
c.TracingEndpoint = "localhost:6831"
|
||||
c.TracesExporter = "console"
|
||||
} else if _, ok := v.(map[string]interface{})["traces_exporter"]; !ok {
|
||||
c.TracesExporter = "console"
|
||||
}
|
||||
|
||||
return c
|
||||
|
||||
@@ -174,9 +174,9 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
|
||||
remoteUser, err := s.ocmClient.InviteAccepted(ctx, ocmEndpoint, &client.InviteAcceptedRequest{
|
||||
Token: req.InviteToken.GetToken(),
|
||||
RecipientProvider: s.conf.ProviderDomain,
|
||||
// The UserID is only a string here. To not loose the IDP information we use the FederatedID encoding
|
||||
// i.e. base64(UserID@IDP)
|
||||
UserID: ocmuser.FederatedID(user.GetId(), "").GetOpaqueId(),
|
||||
// The UserID is only a string here. To not lose the IDP information we use the LocalUserFederatedID encoding
|
||||
// i.e. UserID@IDP
|
||||
UserID: ocmuser.FormatOCMUser(user.GetId()),
|
||||
Email: user.GetMail(),
|
||||
Name: user.GetDisplayName(),
|
||||
})
|
||||
@@ -217,6 +217,9 @@ func (s *service) ForwardInvite(ctx context.Context, req *invitepb.ForwardInvite
|
||||
OpaqueId: remoteUser.UserID,
|
||||
}
|
||||
|
||||
// we need to use a unique identifier for federated users
|
||||
remoteUserID = ocmuser.LocalUserFederatedID(remoteUserID, "")
|
||||
|
||||
if err := s.repo.AddRemoteUser(ctx, user.Id, &userpb.User{
|
||||
Id: remoteUserID,
|
||||
Mail: remoteUser.Email,
|
||||
@@ -276,6 +279,8 @@ func (s *service) AcceptInvite(ctx context.Context, req *invitepb.AcceptInviteRe
|
||||
}
|
||||
|
||||
remoteUser := req.GetRemoteUser()
|
||||
// we need to use a unique identifier for federated users
|
||||
remoteUser.Id = ocmuser.LocalUserFederatedID(remoteUser.Id, "")
|
||||
|
||||
if err := s.repo.AddRemoteUser(ctx, token.GetUserId(), remoteUser); err != nil {
|
||||
if !errors.Is(err, invite.ErrUserAlreadyAccepted) {
|
||||
|
||||
@@ -324,11 +324,11 @@ func (s *service) CreateOCMShare(ctx context.Context, req *ocm.CreateOCMShareReq
|
||||
|
||||
// 2.b replace outgoing user ids with ocm user ids
|
||||
// unpack the federated user id
|
||||
shareWith := ocmuser.FormatOCMUser(ocmuser.RemoteID(req.GetGrantee().GetUserId()))
|
||||
shareWith := ocmuser.FormatOCMUser(ocmuser.LocalUserFederatedID(req.GetGrantee().GetUserId(), ""))
|
||||
|
||||
// wrap the local user id in a federated user id
|
||||
owner := ocmuser.FormatOCMUser(ocmuser.FederatedID(info.Owner, s.conf.ProviderDomain))
|
||||
sender := ocmuser.FormatOCMUser(ocmuser.FederatedID(user.Id, s.conf.ProviderDomain))
|
||||
// wrap the local user id in a local federated user id
|
||||
owner := ocmuser.FormatOCMUser(ocmuser.LocalUserFederatedID(info.Owner, s.conf.ProviderDomain))
|
||||
sender := ocmuser.FormatOCMUser(ocmuser.LocalUserFederatedID(user.Id, s.conf.ProviderDomain))
|
||||
|
||||
newShareReq := &client.NewShareRequest{
|
||||
ShareWith: shareWith,
|
||||
|
||||
148
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/client.go
generated
vendored
Normal file
148
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/client.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2018-2025 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package ocmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/internal/http/services/wellknown"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// OCMClient is the client for an OCM provider.
|
||||
type OCMClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a new OCMClient.
|
||||
func NewClient(timeout time.Duration, insecure bool) *OCMClient {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
|
||||
}
|
||||
return &OCMClient{
|
||||
client: &http.Client{
|
||||
Transport: tr,
|
||||
Timeout: timeout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Discover returns the OCM discovery information for a remote endpoint.
|
||||
// It tries /.well-known/ocm first, then falls back to /ocm-provider (legacy).
|
||||
// https://cs3org.github.io/OCM-API/docs.html?branch=develop&repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
|
||||
func (c *OCMClient) Discover(ctx context.Context, endpoint string) (*wellknown.OcmDiscoveryData, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
remoteurl, _ := url.JoinPath(endpoint, "/.well-known/ocm")
|
||||
body, err := c.discover(ctx, remoteurl)
|
||||
if err != nil || len(body) == 0 {
|
||||
log.Debug().Err(err).Str("sender", remoteurl).Str("response", string(body)).
|
||||
Msg("invalid or empty response, falling back to legacy discovery")
|
||||
remoteurl, _ := url.JoinPath(endpoint, "/ocm-provider") // legacy discovery endpoint
|
||||
|
||||
body, err = c.discover(ctx, remoteurl)
|
||||
if err != nil || len(body) == 0 {
|
||||
log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).
|
||||
Msg("invalid or empty response")
|
||||
return nil, errtypes.InternalError("Invalid response on OCM discovery")
|
||||
}
|
||||
}
|
||||
|
||||
var disco wellknown.OcmDiscoveryData
|
||||
err = json.Unmarshal(body, &disco)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("sender", remoteurl).Str("response", string(body)).
|
||||
Msg("malformed response")
|
||||
return nil, errtypes.InternalError("Invalid payload on OCM discovery")
|
||||
}
|
||||
|
||||
log.Debug().Str("sender", remoteurl).Any("response", disco).Msg("discovery response")
|
||||
return &disco, nil
|
||||
}
|
||||
|
||||
func (c *OCMClient) discover(ctx context.Context, url string) ([]byte, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error creating OCM discovery request")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
defer func() {
|
||||
if resp != nil && resp.Body != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error doing OCM discovery request")
|
||||
}
|
||||
defer func(body io.ReadCloser) {
|
||||
err := body.Close()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("error closing response body")
|
||||
}
|
||||
}(resp.Body)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Warn().Str("sender", url).Int("status", resp.StatusCode).Msg("discovery returned")
|
||||
return nil, errtypes.NewErrtypeFromHTTPStatusCode(resp.StatusCode, "Remote does not offer a valid OCM discovery endpoint")
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "malformed remote OCM discovery")
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// GetDirectoryService fetches a directory service listing from the given URL per OCM spec Appendix C.
|
||||
func (c *OCMClient) GetDirectoryService(ctx context.Context, directoryURL string) (*DirectoryService, error) {
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
// TODO(@MahdiBaghbani): the discover() should be changed into a generic function that can be used to fetch any OCM endpoint. I'll do it in the security PR to minimize conflicts.
|
||||
body, err := c.discover(ctx, directoryURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error fetching directory service")
|
||||
}
|
||||
|
||||
var dirService DirectoryService
|
||||
if err := json.Unmarshal(body, &dirService); err != nil {
|
||||
log.Warn().Err(err).Str("url", directoryURL).Str("response", string(body)).Msg("malformed directory service response")
|
||||
return nil, errors.Wrap(err, "invalid directory service payload")
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if dirService.Federation == "" {
|
||||
return nil, errtypes.InternalError("directory service missing required 'federation' field")
|
||||
}
|
||||
// Servers can be empty array, that's valid
|
||||
|
||||
log.Debug().Str("url", directoryURL).Str("federation", dirService.Federation).Int("servers", len(dirService.Servers)).Msg("fetched directory service")
|
||||
return &dirService, nil
|
||||
}
|
||||
6
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/invites.go
generated
vendored
6
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/invites.go
generated
vendored
@@ -60,7 +60,7 @@ type acceptInviteRequest struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// AcceptInvite informs avout an accepted invitation so that the users
|
||||
// AcceptInvite informs about an accepted invitation so that the users
|
||||
// can initiate the OCM share creation.
|
||||
func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -73,7 +73,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if req.Token == "" || req.UserID == "" || req.RecipientProvider == "" {
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipiendProvider must not be null", nil)
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "token, userID and recipientProvider must not be null", nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ func (h *invitesHandler) AcceptInvite(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(&user{
|
||||
UserID: ocmuser.FederatedID(acceptInviteResponse.UserId, "").GetOpaqueId(),
|
||||
UserID: ocmuser.FormatOCMUser(acceptInviteResponse.UserId),
|
||||
Email: acceptInviteResponse.Email,
|
||||
Name: acceptInviteResponse.DisplayName,
|
||||
}); err != nil {
|
||||
|
||||
33
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/sepc.go
generated
vendored
Normal file
33
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/sepc.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2025 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package ocmd
|
||||
|
||||
// DirectoryService represents a directory service listing per OCM spec Appendix C.
|
||||
type DirectoryService struct {
|
||||
Federation string `json:"federation"`
|
||||
Servers []DirectoryServiceServer `json:"servers"`
|
||||
}
|
||||
|
||||
// DirectoryServiceServer represents a single OCM server in a directory service.
|
||||
type DirectoryServiceServer struct {
|
||||
DisplayName string `json:"displayName"`
|
||||
URL string `json:"url"`
|
||||
// Added after discovery, not in raw response
|
||||
InviteAcceptDialog string `json:"inviteAcceptDialog,omitempty"`
|
||||
}
|
||||
33
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/shares.go
generated
vendored
33
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/ocmd/shares.go
generated
vendored
@@ -126,7 +126,7 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
shareWith, _, err := getIDAndMeshProvider(req.ShareWith)
|
||||
shareWith, _, err := getLocalUserID(req.ShareWith)
|
||||
if err != nil {
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, err.Error(), nil)
|
||||
return
|
||||
@@ -196,6 +196,22 @@ func (h *sharesHandler) CreateShare(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
|
||||
func getLocalUserID(user string) (id, provider string, err error) {
|
||||
idPart, provider, err := getIDAndMeshProvider(user)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Handle nested @ in idPart (e.g. "user@idp@provider")
|
||||
if inner := strings.LastIndex(idPart, "@"); inner != -1 {
|
||||
id = idPart[:inner]
|
||||
} else {
|
||||
id = idPart
|
||||
}
|
||||
|
||||
return id, provider, nil
|
||||
}
|
||||
|
||||
func getUserIDFromOCMUser(user string) (*userpb.UserId, error) {
|
||||
id, idp, err := getIDAndMeshProvider(user)
|
||||
if err != nil {
|
||||
@@ -209,13 +225,18 @@ func getUserIDFromOCMUser(user string) (*userpb.UserId, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getIDAndMeshProvider(user string) (string, string, error) {
|
||||
// the user is in the form of dimitri@apiwise.nl
|
||||
split := strings.Split(user, "@")
|
||||
if len(split) < 2 {
|
||||
func getIDAndMeshProvider(user string) (id, provider string, err error) {
|
||||
last := strings.LastIndex(user, "@")
|
||||
if last == -1 {
|
||||
return "", "", errors.New("not in the form <id>@<provider>")
|
||||
}
|
||||
return strings.Join(split[:len(split)-1], "@"), split[len(split)-1], nil
|
||||
if len(user[:last]) == 0 {
|
||||
return "", "", errors.New("empty id")
|
||||
}
|
||||
if len(user[last+1:]) == 0 {
|
||||
return "", "", errors.New("empty provider")
|
||||
}
|
||||
return user[:last], user[last+1:], nil
|
||||
}
|
||||
|
||||
func getCreateShareRequest(r *http.Request) (*createShareRequest, error) {
|
||||
|
||||
@@ -244,13 +244,30 @@ func (h *Handler) mapUserIdsFederatedShare(ctx context.Context, gw gatewayv1beta
|
||||
}
|
||||
}
|
||||
|
||||
func getIDAndMeshProvider(user string) (id, provider string, err error) {
|
||||
last := strings.LastIndex(user, "@")
|
||||
if last == -1 {
|
||||
return "", "", errors.New("not in the form <id>@<provider>")
|
||||
}
|
||||
if len(user[:last]) == 0 {
|
||||
return "", "", errors.New("empty id")
|
||||
}
|
||||
if len(user[last+1:]) == 0 {
|
||||
return "", "", errors.New("empty provider")
|
||||
}
|
||||
return user[:last], user[last+1:], nil
|
||||
}
|
||||
|
||||
func (h *Handler) mustGetRemoteUser(ctx context.Context, gw gatewayv1beta1.GatewayAPIClient, id string) *userIdentifiers {
|
||||
s := strings.SplitN(id, "@", 2)
|
||||
opaqueID, idp := s[0], s[1]
|
||||
id, provider, err := getIDAndMeshProvider(id)
|
||||
if err != nil {
|
||||
return &userIdentifiers{}
|
||||
}
|
||||
userRes, err := gw.GetAcceptedUser(ctx, &invitepb.GetAcceptedUserRequest{
|
||||
RemoteUserId: &userpb.UserId{
|
||||
Idp: idp,
|
||||
OpaqueId: opaqueID,
|
||||
Type: userpb.UserType_USER_TYPE_FEDERATED,
|
||||
Idp: provider,
|
||||
OpaqueId: id,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
27
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/sciencemesh/sciencemesh.go
generated
vendored
27
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/sciencemesh/sciencemesh.go
generated
vendored
@@ -60,12 +60,15 @@ func (s *svc) Close() error {
|
||||
}
|
||||
|
||||
type config struct {
|
||||
Prefix string `mapstructure:"prefix"`
|
||||
GatewaySvc string `mapstructure:"gatewaysvc" validate:"required"`
|
||||
ProviderDomain string `mapstructure:"provider_domain" validate:"required"`
|
||||
MeshDirectoryURL string `mapstructure:"mesh_directory_url"`
|
||||
OCMMountPoint string `mapstructure:"ocm_mount_point"`
|
||||
Events EventOptions `mapstructure:"events"`
|
||||
Prefix string `mapstructure:"prefix"`
|
||||
GatewaySvc string `mapstructure:"gatewaysvc" validate:"required"`
|
||||
ProviderDomain string `mapstructure:"provider_domain" validate:"required"`
|
||||
MeshDirectoryURL string `mapstructure:"mesh_directory_url"`
|
||||
OCMMountPoint string `mapstructure:"ocm_mount_point"`
|
||||
DirectoryServiceURLs string `mapstructure:"directory_service_urls"`
|
||||
OCMClientTimeout int `mapstructure:"ocm_client_timeout"`
|
||||
OCMClientInsecure bool `mapstructure:"ocm_client_insecure"`
|
||||
Events EventOptions `mapstructure:"events"`
|
||||
}
|
||||
|
||||
// EventOptions are the configurable options for events
|
||||
@@ -86,6 +89,9 @@ func (c *config) ApplyDefaults() {
|
||||
if c.OCMMountPoint == "" {
|
||||
c.OCMMountPoint = "/ocm"
|
||||
}
|
||||
if c.OCMClientTimeout == 0 {
|
||||
c.OCMClientTimeout = 10
|
||||
}
|
||||
|
||||
c.GatewaySvc = sharedconf.GetGatewaySVC(c.GatewaySvc)
|
||||
}
|
||||
@@ -114,6 +120,11 @@ func (s *svc) routerInit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
wayfHandler := new(wayfHandler)
|
||||
if err := wayfHandler.init(s.conf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.router.Post("/generate-invite", tokenHandler.Generate)
|
||||
s.router.Get("/list-invite", tokenHandler.ListInvite)
|
||||
s.router.Post("/accept-invite", tokenHandler.AcceptInvite)
|
||||
@@ -122,6 +133,8 @@ func (s *svc) routerInit() error {
|
||||
s.router.Get("/list-providers", providersHandler.ListProviders)
|
||||
s.router.Post("/create-share", sharesHandler.CreateShare)
|
||||
s.router.Post("/open-in-app", appsHandler.OpenInApp)
|
||||
s.router.Get("/federations", wayfHandler.GetFederations)
|
||||
s.router.Post("/discover", wayfHandler.DiscoverProvider)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -130,7 +143,7 @@ func (s *svc) Prefix() string {
|
||||
}
|
||||
|
||||
func (s *svc) Unprotected() []string {
|
||||
return nil
|
||||
return []string{"/federations", "/discover"}
|
||||
}
|
||||
|
||||
func (s *svc) Handler() http.Handler {
|
||||
|
||||
259
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/sciencemesh/wayf.go
generated
vendored
Normal file
259
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/sciencemesh/wayf.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright 2018-2024 CERN
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// In applying this license, CERN does not waive the privileges and immunities
|
||||
// granted to it by virtue of its status as an Intergovernmental Organization
|
||||
// or submit itself to any jurisdiction.
|
||||
|
||||
package sciencemesh
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/internal/http/services/ocmd"
|
||||
"github.com/opencloud-eu/reva/v2/internal/http/services/reqres"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
)
|
||||
|
||||
type wayfHandler struct {
|
||||
directoryServices []ocmd.DirectoryService
|
||||
ocmClient *ocmd.OCMClient
|
||||
}
|
||||
|
||||
type DiscoverRequest struct {
|
||||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
type DiscoverResponse struct {
|
||||
InviteAcceptDialog string `json:"inviteAcceptDialog"`
|
||||
}
|
||||
|
||||
// makeAbsoluteURL takes a base URL and a path/URL and returns an absolute URL.
|
||||
// If dialogURL is already absolute (has scheme and host), it returns it as-is.
|
||||
// Otherwise, it joins the dialogURL with the baseURL to create an absolute URL.
|
||||
func makeAbsoluteURL(baseURL, dialogURL string) (string, error) {
|
||||
if dialogURL == "" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(dialogURL)
|
||||
if err == nil && parsed.Scheme != "" && parsed.Host != "" {
|
||||
return dialogURL, nil
|
||||
}
|
||||
|
||||
return url.JoinPath(baseURL, dialogURL)
|
||||
}
|
||||
|
||||
func (h *wayfHandler) init(c *config) error {
|
||||
log := appctx.GetLogger(context.Background())
|
||||
|
||||
// Create OCM client for discovery from config
|
||||
h.ocmClient = ocmd.NewClient(time.Duration(c.OCMClientTimeout)*time.Second, c.OCMClientInsecure)
|
||||
log.Debug().
|
||||
Int("timeout_seconds", c.OCMClientTimeout).
|
||||
Bool("insecure", c.OCMClientInsecure).
|
||||
Msg("Created OCM client for discovery")
|
||||
|
||||
urls := strings.Fields(c.DirectoryServiceURLs)
|
||||
if len(urls) == 0 {
|
||||
log.Info().Msg("No directory service URLs configured, starting with empty list")
|
||||
h.directoryServices = []ocmd.DirectoryService{}
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug().Int("url_count", len(urls)).Strs("urls", urls).Msg("Initializing WAYF handler with directory service URLs")
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
h.directoryServices = []ocmd.DirectoryService{}
|
||||
discoveryErrors := 0
|
||||
validServersCount := 0
|
||||
fetchErrors := 0
|
||||
|
||||
for _, directoryURL := range urls {
|
||||
log.Debug().Str("url", directoryURL).Msg("Fetching directory service")
|
||||
|
||||
directoryService, err := h.ocmClient.GetDirectoryService(ctx, directoryURL)
|
||||
if err != nil {
|
||||
log.Info().Err(err).Str("url", directoryURL).Msg("Failed to fetch directory service, skipping")
|
||||
fetchErrors++
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug().Str("federation", directoryService.Federation).Int("servers_count", len(directoryService.Servers)).Msg("Processing directory service")
|
||||
|
||||
var validServers []ocmd.DirectoryServiceServer
|
||||
for _, srv := range directoryService.Servers {
|
||||
if srv.DisplayName == "" || srv.URL == "" {
|
||||
log.Debug().Str("federation", directoryService.Federation).
|
||||
Str("displayName", srv.DisplayName).
|
||||
Str("url", srv.URL).
|
||||
Msg("Skipping server with missing displayName or url")
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug().Str("federation", directoryService.Federation).Str("server", srv.DisplayName).Str("url", srv.URL).Msg("Discovering server")
|
||||
|
||||
// Discover inviteAcceptDialog from OCM endpoint
|
||||
disco, err := h.ocmClient.Discover(ctx, srv.URL)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).
|
||||
Str("federation", directoryService.Federation).
|
||||
Str("server", srv.DisplayName).
|
||||
Str("url", srv.URL).
|
||||
Msg("Failed to discover server, skipping")
|
||||
discoveryErrors++
|
||||
continue
|
||||
}
|
||||
|
||||
inviteDialog := disco.InviteAcceptDialog
|
||||
|
||||
if inviteDialog != "" {
|
||||
absoluteURL, err := makeAbsoluteURL(srv.URL, inviteDialog)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).
|
||||
Str("federation", directoryService.Federation).
|
||||
Str("server", srv.DisplayName).
|
||||
Str("url", srv.URL).
|
||||
Str("inviteDialog", disco.InviteAcceptDialog).
|
||||
Msg("Failed to construct absolute URL, skipping server")
|
||||
continue
|
||||
}
|
||||
if absoluteURL != inviteDialog {
|
||||
log.Debug().Str("original", inviteDialog).Str("absolute", absoluteURL).Msg("Converted to absolute URL")
|
||||
}
|
||||
inviteDialog = absoluteURL
|
||||
}
|
||||
|
||||
validServers = append(validServers, ocmd.DirectoryServiceServer{
|
||||
DisplayName: srv.DisplayName,
|
||||
URL: srv.URL,
|
||||
InviteAcceptDialog: inviteDialog,
|
||||
})
|
||||
validServersCount++
|
||||
|
||||
log.Debug().
|
||||
Str("federation", directoryService.Federation).
|
||||
Str("server", srv.DisplayName).
|
||||
Str("inviteAcceptDialog", inviteDialog).
|
||||
Msg("Successfully discovered server")
|
||||
}
|
||||
|
||||
if len(validServers) > 0 {
|
||||
h.directoryServices = append(h.directoryServices, ocmd.DirectoryService{
|
||||
Federation: directoryService.Federation,
|
||||
Servers: validServers,
|
||||
})
|
||||
log.Debug().Str("federation", directoryService.Federation).Int("valid_servers", len(validServers)).Msg("Added directory service with valid servers")
|
||||
} else {
|
||||
log.Info().Str("federation", directoryService.Federation).
|
||||
Msg("Directory service has no valid servers, skipping entirely")
|
||||
}
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Int("directory_services", len(h.directoryServices)).
|
||||
Int("valid_servers", validServersCount).
|
||||
Int("fetch_errors", fetchErrors).
|
||||
Int("discovery_errors", discoveryErrors).
|
||||
Msg("WAYF handler initialization completed")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *wayfHandler) GetFederations(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(h.directoryServices); err != nil {
|
||||
reqres.WriteError(w, r, reqres.APIErrorServerError, "error encoding response", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *wayfHandler) DiscoverProvider(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
log := appctx.GetLogger(ctx)
|
||||
|
||||
var req DiscoverRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "Invalid request body", err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Domain == "" {
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "Domain is required", nil)
|
||||
return
|
||||
}
|
||||
|
||||
domain := req.Domain
|
||||
if !strings.HasPrefix(domain, "http://") && !strings.HasPrefix(domain, "https://") {
|
||||
domain = "https://" + domain
|
||||
}
|
||||
|
||||
parsedURL, err := url.Parse(domain)
|
||||
if err != nil || parsedURL.Host == "" {
|
||||
reqres.WriteError(w, r, reqres.APIErrorInvalidParameter, "Invalid domain format", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug().Str("domain", domain).Msg("Attempting OCM discovery")
|
||||
disco, err := h.ocmClient.Discover(ctx, domain)
|
||||
if err != nil {
|
||||
log.Info().Err(err).Str("domain", domain).Msg("Discovery failed")
|
||||
reqres.WriteError(w, r, reqres.APIErrorNotFound,
|
||||
fmt.Sprintf("Provider at '%s' does not support OCM discovery", req.Domain), err)
|
||||
return
|
||||
}
|
||||
|
||||
inviteDialog := disco.InviteAcceptDialog
|
||||
|
||||
if inviteDialog == "" {
|
||||
log.Info().Str("domain", domain).Msg("Provider does not provide invite accept dialog")
|
||||
reqres.WriteError(w, r, reqres.APIErrorNotFound,
|
||||
fmt.Sprintf("Provider at '%s' does not provide an invite accept dialog", req.Domain), nil)
|
||||
return
|
||||
}
|
||||
|
||||
inviteDialog, err = makeAbsoluteURL(domain, inviteDialog)
|
||||
if err != nil {
|
||||
log.Info().Err(err).Str("domain", domain).Str("inviteDialog", disco.InviteAcceptDialog).Msg("Failed to construct invite accept dialog URL")
|
||||
reqres.WriteError(w, r, reqres.APIErrorServerError, "Failed to construct invite accept dialog URL", err)
|
||||
return
|
||||
}
|
||||
|
||||
response := DiscoverResponse{
|
||||
InviteAcceptDialog: inviteDialog,
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Str("domain", req.Domain).
|
||||
Str("inviteAcceptDialog", inviteDialog).
|
||||
Msg("Discovery successful")
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
reqres.WriteError(w, r, reqres.APIErrorServerError, "Error encoding response", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
40
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/wellknown/ocm.go
generated
vendored
40
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/wellknown/ocm.go
generated
vendored
@@ -27,25 +27,27 @@ import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
)
|
||||
|
||||
const OCMAPIVersion = "1.1.0"
|
||||
const OCMAPIVersion = "1.2.0"
|
||||
|
||||
type OcmProviderConfig struct {
|
||||
OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"`
|
||||
Endpoint string `docs:"This host's full URL. If it's not configured, it is assumed OCM is not available." mapstructure:"endpoint"`
|
||||
Provider string `docs:"reva;A friendly name that defines this service." mapstructure:"provider"`
|
||||
WebdavRoot string `docs:"/remote.php/dav/ocm;The root URL of the WebDAV endpoint to serve OCM shares." mapstructure:"webdav_root"`
|
||||
WebappRoot string `docs:"/external/sciencemesh;The root URL to serve Web apps via OCM." mapstructure:"webapp_root"`
|
||||
EnableWebapp bool `docs:"false;Whether web apps are enabled in OCM shares." mapstructure:"enable_webapp"`
|
||||
EnableDatatx bool `docs:"false;Whether data transfers are enabled in OCM shares." mapstructure:"enable_datatx"`
|
||||
OCMPrefix string `docs:"ocm;The prefix URL where the OCM API is served." mapstructure:"ocm_prefix"`
|
||||
Endpoint string `docs:"This host's full URL. If it's not configured, it is assumed OCM is not available." mapstructure:"endpoint"`
|
||||
Provider string `docs:"reva;A friendly name that defines this service." mapstructure:"provider"`
|
||||
WebdavRoot string `docs:"/remote.php/dav/ocm;The root URL of the WebDAV endpoint to serve OCM shares." mapstructure:"webdav_root"`
|
||||
WebappRoot string `docs:"/external/sciencemesh;The root URL to serve Web apps via OCM." mapstructure:"webapp_root"`
|
||||
InviteAcceptDialog string `docs:"/open-cloud-mesh/accept-invite;The frontend URL where to land when receiving an invitation" mapstructure:"invite_accept_dialog"`
|
||||
EnableWebapp bool `docs:"false;Whether web apps are enabled in OCM shares." mapstructure:"enable_webapp"`
|
||||
EnableDatatx bool `docs:"false;Whether data transfers are enabled in OCM shares." mapstructure:"enable_datatx"`
|
||||
}
|
||||
|
||||
type OcmDiscoveryData struct {
|
||||
Enabled bool `json:"enabled" xml:"enabled"`
|
||||
APIVersion string `json:"apiVersion" xml:"apiVersion"`
|
||||
Endpoint string `json:"endPoint" xml:"endPoint"`
|
||||
Provider string `json:"provider" xml:"provider"`
|
||||
ResourceTypes []resourceTypes `json:"resourceTypes" xml:"resourceTypes"`
|
||||
Capabilities []string `json:"capabilities" xml:"capabilities"`
|
||||
Enabled bool `json:"enabled" xml:"enabled"`
|
||||
APIVersion string `json:"apiVersion" xml:"apiVersion"`
|
||||
Endpoint string `json:"endPoint" xml:"endPoint"`
|
||||
Provider string `json:"provider" xml:"provider"`
|
||||
ResourceTypes []resourceTypes `json:"resourceTypes" xml:"resourceTypes"`
|
||||
Capabilities []string `json:"capabilities" xml:"capabilities"`
|
||||
InviteAcceptDialog string `json:"inviteAcceptDialog" xml:"inviteAcceptDialog"`
|
||||
}
|
||||
|
||||
type resourceTypes struct {
|
||||
@@ -77,6 +79,9 @@ func (c *OcmProviderConfig) ApplyDefaults() {
|
||||
if c.WebappRoot[len(c.WebappRoot)-1:] != "/" {
|
||||
c.WebappRoot += "/"
|
||||
}
|
||||
if c.InviteAcceptDialog == "" {
|
||||
c.InviteAcceptDialog = "/open-cloud-mesh/accept-invite"
|
||||
}
|
||||
}
|
||||
|
||||
func (h *wkocmHandler) init(c *OcmProviderConfig) {
|
||||
@@ -123,12 +128,13 @@ func (h *wkocmHandler) init(c *OcmProviderConfig) {
|
||||
ShareTypes: []string{"user"}, // so far we only support `user`
|
||||
Protocols: rtProtos, // expose the protocols as per configuration
|
||||
}}
|
||||
// for now we hardcode the capabilities, as this is currently only advisory
|
||||
d.Capabilities = []string{"/invite-accepted"}
|
||||
// for now, we hardcoded the capabilities, as this is currently only advisory
|
||||
d.Capabilities = []string{"invites", "webdav-uri", "protocol-object", "invite-wayf"}
|
||||
d.InviteAcceptDialog, _ = url.JoinPath(c.Endpoint, c.InviteAcceptDialog)
|
||||
h.data = d
|
||||
}
|
||||
|
||||
// This handler implements the OCM discovery endpoint specified in
|
||||
// Ocm handles the OCM discovery endpoint specified in
|
||||
// https://cs3org.github.io/OCM-API/docs.html?repo=OCM-API&user=cs3org#/paths/~1ocm-provider/get
|
||||
func (h *wkocmHandler) Ocm(w http.ResponseWriter, r *http.Request) {
|
||||
log := appctx.GetLogger(r.Context())
|
||||
|
||||
27
vendor/github.com/opencloud-eu/reva/v2/pkg/events/events.go
generated
vendored
27
vendor/github.com/opencloud-eu/reva/v2/pkg/events/events.go
generated
vendored
@@ -85,7 +85,19 @@ type (
|
||||
// group defines the service type: One group will get exactly one copy of a event that is emitted
|
||||
// NOTE: uses reflect on initialization
|
||||
func Consume(s Consumer, group string, evs ...Unmarshaller) (<-chan Event, error) {
|
||||
c, err := s.Consume(MainQueueName, events.WithGroup(group))
|
||||
return ConsumeWithOptions(s, ConsumeOptions{Group: group}, evs...)
|
||||
}
|
||||
|
||||
// ConsumeWithOptions returns a channel that will get all events that match the given evs
|
||||
// opts defines the options for the consumer
|
||||
func ConsumeWithOptions(s Consumer, opts ConsumeOptions, evs ...Unmarshaller) (<-chan Event, error) {
|
||||
o := []events.ConsumeOption{
|
||||
events.WithGroup(opts.Group),
|
||||
}
|
||||
if !opts.AutoAck {
|
||||
o = append(o, events.WithAutoAck(false, opts.AckWait))
|
||||
}
|
||||
c, err := s.Consume(MainQueueName, o...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -126,7 +138,18 @@ func Consume(s Consumer, group string, evs ...Unmarshaller) (<-chan Event, error
|
||||
|
||||
// ConsumeAll allows consuming all events. Note that unmarshalling must be done manually in this case, therefore Event.Event will always be of type []byte
|
||||
func ConsumeAll(s Consumer, group string) (<-chan Event, error) {
|
||||
c, err := s.Consume(MainQueueName, events.WithGroup(group))
|
||||
return ConsumeAllWithOptions(s, ConsumeOptions{Group: group})
|
||||
}
|
||||
|
||||
// ConsumeAllWithOptions allows consuming all events. Note that unmarshalling must be done manually in this case, therefore Event.Event will always be of type []byte
|
||||
func ConsumeAllWithOptions(s Consumer, opts ConsumeOptions) (<-chan Event, error) {
|
||||
o := []events.ConsumeOption{
|
||||
events.WithGroup(opts.Group),
|
||||
}
|
||||
if !opts.AutoAck {
|
||||
o = append(o, events.WithAutoAck(false, opts.AckWait))
|
||||
}
|
||||
c, err := s.Consume(MainQueueName, o...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
12
vendor/github.com/opencloud-eu/reva/v2/pkg/events/options.go
generated
vendored
Normal file
12
vendor/github.com/opencloud-eu/reva/v2/pkg/events/options.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ConsumeOptions contains all the options which can be provided when consuming events
|
||||
type ConsumeOptions struct {
|
||||
Group string
|
||||
AutoAck bool
|
||||
AckWait time.Duration
|
||||
}
|
||||
20
vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go
generated
vendored
20
vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go
generated
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
@@ -331,7 +332,11 @@ func (m *manager) ldapEntryToGroup(entry *ldap.Entry) (*grouppb.Group, error) {
|
||||
func (m *manager) ldapEntryToGroupID(entry *ldap.Entry) (*grouppb.GroupId, error) {
|
||||
var id string
|
||||
if m.c.LDAPIdentity.Group.Schema.IDIsOctetString {
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(m.c.LDAPIdentity.Group.Schema.ID)
|
||||
attribute := m.c.LDAPIdentity.Group.Schema.ID
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(attribute)
|
||||
if strings.EqualFold(attribute, "objectguid") {
|
||||
rawValue = ldapIdentity.SwapObjectGUIDBytes(rawValue)
|
||||
}
|
||||
if value, err := uuid.FromBytes(rawValue); err == nil {
|
||||
id = value.String()
|
||||
} else {
|
||||
@@ -350,13 +355,16 @@ func (m *manager) ldapEntryToGroupID(entry *ldap.Entry) (*grouppb.GroupId, error
|
||||
func (m *manager) ldapEntryToUserID(entry *ldap.Entry) (*userpb.UserId, error) {
|
||||
var uid string
|
||||
if m.c.LDAPIdentity.User.Schema.IDIsOctetString {
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(m.c.LDAPIdentity.User.Schema.ID)
|
||||
var value uuid.UUID
|
||||
var err error
|
||||
if value, err = uuid.FromBytes(rawValue); err != nil {
|
||||
attribute := m.c.LDAPIdentity.User.Schema.ID
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(attribute)
|
||||
if strings.EqualFold(attribute, "objectguid") {
|
||||
rawValue = ldapIdentity.SwapObjectGUIDBytes(rawValue)
|
||||
}
|
||||
if value, err := uuid.FromBytes(rawValue); err == nil {
|
||||
uid = value.String()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
uid = value.String()
|
||||
} else {
|
||||
uid = entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.ID)
|
||||
}
|
||||
|
||||
42
vendor/github.com/opencloud-eu/reva/v2/pkg/micro/ocdav/option.go
generated
vendored
42
vendor/github.com/opencloud-eu/reva/v2/pkg/micro/ocdav/option.go
generated
vendored
@@ -31,7 +31,6 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"go-micro.dev/v4/broker"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
// Option defines a single option function.
|
||||
@@ -52,10 +51,7 @@ type Options struct {
|
||||
FavoriteManager favorite.Manager
|
||||
GatewaySelector pool.Selectable[gateway.GatewayAPIClient]
|
||||
|
||||
TracingEnabled bool
|
||||
TracingInsecure bool
|
||||
TracingEndpoint string
|
||||
TracingTransportCredentials credentials.TransportCredentials
|
||||
TracesExporter string
|
||||
|
||||
TraceProvider trace.TracerProvider
|
||||
|
||||
@@ -233,33 +229,10 @@ func LockSystem(val ocdav.LockSystem) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// Tracing enables tracing
|
||||
// Deprecated: use WithTracingEndpoint and WithTracingEnabled, Collector is unused
|
||||
func Tracing(endpoint, collector string) Option {
|
||||
// WithTracesExporter option
|
||||
func WithTracesExporter(exporter string) Option {
|
||||
return func(o *Options) {
|
||||
o.TracingEnabled = true
|
||||
o.TracingEndpoint = endpoint
|
||||
}
|
||||
}
|
||||
|
||||
// WithTracingEnabled option
|
||||
func WithTracingEnabled(enabled bool) Option {
|
||||
return func(o *Options) {
|
||||
o.TracingEnabled = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// WithTracingEndpoint option
|
||||
func WithTracingEndpoint(endpoint string) Option {
|
||||
return func(o *Options) {
|
||||
o.TracingEndpoint = endpoint
|
||||
}
|
||||
}
|
||||
|
||||
// WithTracingInsecure option
|
||||
func WithTracingInsecure() Option {
|
||||
return func(o *Options) {
|
||||
o.TracingInsecure = true
|
||||
o.TracesExporter = exporter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,13 +242,6 @@ func WithTracingExporter(exporter string) Option {
|
||||
return func(o *Options) {}
|
||||
}
|
||||
|
||||
// WithTracingTransportCredentials option
|
||||
func WithTracingTransportCredentials(v credentials.TransportCredentials) Option {
|
||||
return func(o *Options) {
|
||||
o.TracingTransportCredentials = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithTraceProvider option
|
||||
func WithTraceProvider(provider trace.TracerProvider) Option {
|
||||
return func(o *Options) {
|
||||
|
||||
20
vendor/github.com/opencloud-eu/reva/v2/pkg/micro/ocdav/service.go
generated
vendored
20
vendor/github.com/opencloud-eu/reva/v2/pkg/micro/ocdav/service.go
generated
vendored
@@ -91,20 +91,7 @@ func Service(opts ...Option) (micro.Service, error) {
|
||||
tp := sopts.TraceProvider
|
||||
|
||||
if tp == nil {
|
||||
topts := []rtrace.Option{
|
||||
rtrace.WithEndpoint(sopts.TracingEndpoint),
|
||||
rtrace.WithServiceName(sopts.Name),
|
||||
}
|
||||
if sopts.TracingEnabled {
|
||||
topts = append(topts, rtrace.WithEnabled())
|
||||
}
|
||||
if sopts.TracingInsecure {
|
||||
topts = append(topts, rtrace.WithInsecure())
|
||||
}
|
||||
if sopts.TracingTransportCredentials != nil {
|
||||
topts = append(topts, rtrace.WithTransportCredentials(sopts.TracingTransportCredentials))
|
||||
}
|
||||
tp = rtrace.NewTracerProvider(topts...)
|
||||
tp = rtrace.NewTracerProvider(sopts.Name, sopts.TracesExporter)
|
||||
}
|
||||
if err := useMiddlewares(r, &sopts, revaService, tp); err != nil {
|
||||
return nil, err
|
||||
@@ -189,10 +176,7 @@ func useMiddlewares(r *chi.Mux, sopts *Options, svc global.Service, tp trace.Tra
|
||||
}
|
||||
|
||||
// tracing
|
||||
tm := func(h http.Handler) http.Handler { return h }
|
||||
if sopts.TracingEnabled {
|
||||
tm = traceHandler(tp, "ocdav")
|
||||
}
|
||||
tm := traceHandler(tp, "ocdav")
|
||||
|
||||
// metrics
|
||||
pm := func(h http.Handler) http.Handler { return h }
|
||||
|
||||
44
vendor/github.com/opencloud-eu/reva/v2/pkg/ocm/user/user.go
generated
vendored
44
vendor/github.com/opencloud-eu/reva/v2/pkg/ocm/user/user.go
generated
vendored
@@ -1,39 +1,44 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
)
|
||||
|
||||
// FederatedID creates a federated user id by
|
||||
// LocalUserFederatedID creates a federated id for local users by
|
||||
// 1. stripping the protocol from the domain and
|
||||
// 2. base64 encoding the opaque id with the domain to get a unique identifier that cannot collide with other users
|
||||
func FederatedID(id *userpb.UserId, domain string) *userpb.UserId {
|
||||
opaqueId := base64.URLEncoding.EncodeToString([]byte(id.OpaqueId + "@" + id.Idp))
|
||||
return &userpb.UserId{
|
||||
Type: userpb.UserType_USER_TYPE_FEDERATED,
|
||||
Idp: domain,
|
||||
OpaqueId: opaqueId,
|
||||
// 2. if the domain is different from the idp, add the idp to the opaque id
|
||||
func LocalUserFederatedID(id *userpb.UserId, domain string) *userpb.UserId {
|
||||
if u, err := url.Parse(domain); err == nil && u.Host != "" {
|
||||
domain = u.Host
|
||||
}
|
||||
u := &userpb.UserId{
|
||||
Type: userpb.UserType_USER_TYPE_FEDERATED,
|
||||
Idp: id.GetIdp(),
|
||||
OpaqueId: id.GetOpaqueId(),
|
||||
}
|
||||
|
||||
if domain != "" && id.GetIdp() != domain {
|
||||
if id.GetIdp() != "" {
|
||||
u.OpaqueId = id.GetOpaqueId() + "@" + id.GetIdp()
|
||||
}
|
||||
u.Idp = domain
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// RemoteID creates a remote user id by
|
||||
// 1. decoding the base64 encoded opaque id
|
||||
// 2. splitting the opaque id at the last @ to get the opaque id and the domain
|
||||
func RemoteID(id *userpb.UserId) *userpb.UserId {
|
||||
// DecodeRemoteUserFederatedID decodes opaque id into remote user's federated id by
|
||||
// splitting the opaque id at the last @ to get the opaque id and the domain
|
||||
func DecodeRemoteUserFederatedID(id *userpb.UserId) *userpb.UserId {
|
||||
remoteId := &userpb.UserId{
|
||||
Type: userpb.UserType_USER_TYPE_PRIMARY,
|
||||
Idp: id.Idp,
|
||||
OpaqueId: id.OpaqueId,
|
||||
}
|
||||
bytes, err := base64.URLEncoding.DecodeString(id.GetOpaqueId())
|
||||
if err != nil {
|
||||
return remoteId
|
||||
}
|
||||
remote := string(bytes)
|
||||
remote := id.OpaqueId
|
||||
last := strings.LastIndex(remote, "@")
|
||||
if last == -1 {
|
||||
return remoteId
|
||||
@@ -46,5 +51,8 @@ func RemoteID(id *userpb.UserId) *userpb.UserId {
|
||||
|
||||
// FormatOCMUser formats a user id in the form of <opaque-id>@<idp> used by the OCM API in shareWith, owner and creator fields
|
||||
func FormatOCMUser(u *userpb.UserId) string {
|
||||
if u.Idp == "" {
|
||||
return u.OpaqueId
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", u.OpaqueId, u.Idp)
|
||||
}
|
||||
|
||||
23
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/assimilation.go
generated
vendored
23
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/assimilation.go
generated
vendored
@@ -328,6 +328,10 @@ func (t *Tree) HandleFileDelete(path string, sendSSE bool) error {
|
||||
t.log.Error().Err(err).Str("path", path).Msg("could not purge metadata")
|
||||
}
|
||||
|
||||
if err = t.setDirty(filepath.Dir(path), true); err != nil {
|
||||
t.log.Error().Err(err).Str("path", path).Msg("could not set dirty flag")
|
||||
}
|
||||
|
||||
if !sendSSE {
|
||||
return nil
|
||||
}
|
||||
@@ -705,6 +709,16 @@ assimilate:
|
||||
attributes.SetInt64(prefixes.BlobsizeAttr, fi.Size())
|
||||
attributes.SetInt64(prefixes.TypeAttr, int64(provider.ResourceType_RESOURCE_TYPE_FILE))
|
||||
n = node.New(spaceID, id, parentID, filepath.Base(path), fi.Size(), blobID, provider.ResourceType_RESOURCE_TYPE_FILE, nil, t.lookup)
|
||||
n.SpaceRoot = &node.Node{BaseNode: node.BaseNode{SpaceID: spaceID, ID: spaceID}}
|
||||
|
||||
prevBlobSize, err := previousAttribs.Int64(prefixes.BlobsizeAttr)
|
||||
if err == nil && prevBlobSize != fi.Size() {
|
||||
// file size changed, trigger propagation of tree size changes
|
||||
err = t.Propagate(context.Background(), n, fi.Size()-prevBlobSize)
|
||||
if err != nil {
|
||||
t.log.Error().Err(err).Str("path", path).Msg("could not propagate tree size changes")
|
||||
}
|
||||
}
|
||||
}
|
||||
attributes.SetTime(prefixes.MTimeAttr, fi.ModTime())
|
||||
|
||||
@@ -772,6 +786,15 @@ assimilate:
|
||||
return nil, nil, errors.Wrap(err, "failed to set attributes")
|
||||
}
|
||||
|
||||
// clear the status attribute if it was set before, if there was any upload to this file in progress
|
||||
// it needs notice that this file was changes meanwhile.
|
||||
if _, ok := previousAttribs[prefixes.StatusPrefix]; ok {
|
||||
err = t.lookup.MetadataBackend().Remove(context.Background(), bn, prefixes.StatusPrefix, false)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "failed to clear status attribute")
|
||||
}
|
||||
|
||||
}
|
||||
if err := t.lookup.CacheID(context.Background(), spaceID, id, path); err != nil {
|
||||
t.log.Error().Err(err).Str("spaceID", spaceID).Str("id", id).Str("path", path).Msg("could not cache id")
|
||||
}
|
||||
|
||||
68
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go
generated
vendored
68
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go
generated
vendored
@@ -35,6 +35,7 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rogpeppe/go-internal/lockedfile"
|
||||
tusd "github.com/tus/tusd/v2/pkg/handler"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -307,11 +308,13 @@ func (session *DecomposedFsSession) Finalize(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// lock the node before writing the blob
|
||||
unlock, err := session.store.lu.MetadataBackend().Lock(revisionNode)
|
||||
lockedNode, err := lockedfile.OpenFile(session.store.lu.MetadataBackend().LockfilePath(revisionNode), os.O_RDWR|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = unlock() }()
|
||||
defer func() {
|
||||
_ = lockedNode.Close()
|
||||
}()
|
||||
|
||||
isProcessing := revisionNode.IsProcessing(ctx)
|
||||
var procssingID string
|
||||
@@ -322,12 +325,18 @@ func (session *DecomposedFsSession) Finalize(ctx context.Context) (err error) {
|
||||
// another upload on this node is in progress or has finished since we started
|
||||
if !isProcessing || procssingID != session.ID() {
|
||||
versionID := revisionNode.ID + node.RevisionIDDelimiter + session.MTime().UTC().Format(time.RFC3339Nano)
|
||||
revisionNode, err = node.ReadNode(ctx, session.store.lu, session.SpaceID(), versionID, false, spaceRoot, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read revision node %s for upload finalization: %w", versionID, err)
|
||||
}
|
||||
if !revisionNode.Exists {
|
||||
return fmt.Errorf("revision node %s for upload finalization does not exist", versionID)
|
||||
// There should be a revision node (created by the other upload that finished before us), read it and upload our blob there.
|
||||
existingRevisionNode, err := node.ReadNode(ctx, session.store.lu, session.SpaceID(), versionID, false, spaceRoot, false)
|
||||
if err != nil || !existingRevisionNode.Exists {
|
||||
// The revision node has not been created. Likely because the file on disk was modified externally and re-assilimated (watchfs == true)
|
||||
// Let's create the revision node now and upload the blob to it.
|
||||
revisionNode, err = session.createRevisionNodeForUpload(ctx, revisionNode, session.MTime().UTC().Format(time.RFC3339Nano), lockedNode)
|
||||
if err != nil {
|
||||
appctx.GetLogger(ctx).Debug().Err(err).Str("versionID", session.MTime().UTC().Format(time.RFC3339Nano)).Msg("failed to create revision node for upload finalization")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
revisionNode = existingRevisionNode
|
||||
}
|
||||
// lock this node as well, before writing the blob
|
||||
revisionNodeUnlock, err := session.store.lu.MetadataBackend().Lock(revisionNode)
|
||||
@@ -349,6 +358,47 @@ func (session *DecomposedFsSession) Finalize(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (session *DecomposedFsSession) createRevisionNodeForUpload(ctx context.Context, baseNode *node.Node, rev string, lockedNode *lockedfile.File) (*node.Node, error) {
|
||||
versionID := baseNode.ID + node.RevisionIDDelimiter + rev
|
||||
log := appctx.GetLogger(ctx)
|
||||
_, err := session.store.tp.CreateRevision(ctx, baseNode, rev, lockedNode)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("versionID", versionID).Msg("failed to create revision node for upload")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// FIXME: We already calculated the checksums in FinishUpload, we should maybe pass them via the session instead of recalculating them here
|
||||
sha1h, md5h, adler32h, err := node.CalculateChecksums(ctx, session.binPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update checksums
|
||||
attrs := node.Attributes{
|
||||
prefixes.ChecksumPrefix + "sha1": sha1h.Sum(nil),
|
||||
prefixes.ChecksumPrefix + "md5": md5h.Sum(nil),
|
||||
prefixes.ChecksumPrefix + "adler32": adler32h.Sum(nil),
|
||||
}
|
||||
revisionNode, err := node.ReadNode(ctx, session.store.lu, session.SpaceID(), versionID, false, baseNode.SpaceRoot, false)
|
||||
if err == nil {
|
||||
mtime := session.MTime()
|
||||
attrs.SetString(prefixes.BlobIDAttr, session.ID())
|
||||
attrs.SetInt64(prefixes.BlobsizeAttr, session.Size())
|
||||
revisionNode.BlobID = session.ID()
|
||||
revisionNode.Blobsize = session.Size()
|
||||
|
||||
err = session.store.lu.TimeManager().OverrideMtime(ctx, revisionNode, &attrs, mtime)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Decomposedfs: failed to set the mtime")
|
||||
}
|
||||
err = revisionNode.SetXattrsWithContext(ctx, attrs, false)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Decomposedfs: failed to set node attributes")
|
||||
}
|
||||
}
|
||||
return revisionNode, err
|
||||
}
|
||||
|
||||
func checkHash(expected string, h hash.Hash) error {
|
||||
shash := hex.EncodeToString(h.Sum(nil))
|
||||
if expected != shash {
|
||||
@@ -389,6 +439,7 @@ func (session *DecomposedFsSession) Cleanup(revertNodeMetadata, cleanBin, cleanI
|
||||
|
||||
if !revisionNode.Exists {
|
||||
sublog.Error().Str("versionID", versionID).Msg("revision node does not exist")
|
||||
return
|
||||
}
|
||||
|
||||
// restore the revision
|
||||
@@ -400,6 +451,7 @@ func (session *DecomposedFsSession) Cleanup(revertNodeMetadata, cleanBin, cleanI
|
||||
|
||||
if err := session.store.tp.RestoreRevision(ctx, revisionNode, n, mtime); err != nil {
|
||||
sublog.Error().Err(err).Str("versionID", versionID).Msg("restoring revision node failed")
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(revisionNode.InternalPath()); err != nil {
|
||||
|
||||
68
vendor/github.com/opencloud-eu/reva/v2/pkg/trace/options.go
generated
vendored
68
vendor/github.com/opencloud-eu/reva/v2/pkg/trace/options.go
generated
vendored
@@ -1,68 +0,0 @@
|
||||
package trace
|
||||
|
||||
import "google.golang.org/grpc/credentials"
|
||||
|
||||
// Options for trace
|
||||
type Options struct {
|
||||
Enabled bool
|
||||
Insecure bool
|
||||
Exporter string
|
||||
Collector string
|
||||
Endpoint string
|
||||
ServiceName string
|
||||
TransportCredentials credentials.TransportCredentials
|
||||
}
|
||||
|
||||
// Option for trace
|
||||
type Option func(o *Options)
|
||||
|
||||
// WithEnabled option
|
||||
func WithEnabled() Option {
|
||||
return func(o *Options) {
|
||||
o.Enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithExporter option
|
||||
// Deprecated: unused
|
||||
func WithExporter(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Exporter = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithInsecure option
|
||||
func WithInsecure() Option {
|
||||
return func(o *Options) {
|
||||
o.Insecure = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithCollector option
|
||||
// Deprecated: unused
|
||||
func WithCollector(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Collector = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithEndpoint option
|
||||
func WithEndpoint(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.Endpoint = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceName option
|
||||
func WithServiceName(v string) Option {
|
||||
return func(o *Options) {
|
||||
o.ServiceName = v
|
||||
}
|
||||
}
|
||||
|
||||
// WithTransportCredentials option
|
||||
func WithTransportCredentials(v credentials.TransportCredentials) Option {
|
||||
return func(o *Options) {
|
||||
o.TransportCredentials = v
|
||||
}
|
||||
}
|
||||
112
vendor/github.com/opencloud-eu/reva/v2/pkg/trace/trace.go
generated
vendored
112
vendor/github.com/opencloud-eu/reva/v2/pkg/trace/trace.go
generated
vendored
@@ -20,21 +20,17 @@ package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.opentelemetry.io/otel/trace/noop"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -48,28 +44,42 @@ type revaDefaultTracerProvider struct {
|
||||
initialized bool
|
||||
}
|
||||
|
||||
// NewTracerProvider returns a new TracerProvider, configure for the specified service
|
||||
func NewTracerProvider(opts ...Option) trace.TracerProvider {
|
||||
options := Options{}
|
||||
// NewTracerProvider returns a new TracerProvider, configured for the specified service
|
||||
func NewTracerProvider(serviceName, exporter string) trace.TracerProvider {
|
||||
var tp *sdktrace.TracerProvider
|
||||
if exporter == "otlp" {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
|
||||
for _, o := range opts {
|
||||
o(&options)
|
||||
exporter, err := otlptracegrpc.New(ctx)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
resources, err := resource.New(
|
||||
context.Background(),
|
||||
resource.WithFromEnv(), // Reads OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME
|
||||
resource.WithAttributes(
|
||||
attribute.String("service.name", serviceName),
|
||||
attribute.String("library.language", "go"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tp = sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||
sdktrace.WithBatcher(exporter),
|
||||
sdktrace.WithResource(resources),
|
||||
)
|
||||
} else {
|
||||
tp = sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.NeverSample()),
|
||||
)
|
||||
}
|
||||
|
||||
if options.TransportCredentials == nil {
|
||||
options.TransportCredentials = credentials.NewClientTLSFromCert(nil, "")
|
||||
}
|
||||
|
||||
if !options.Enabled {
|
||||
return noop.NewTracerProvider()
|
||||
}
|
||||
|
||||
// default to 'reva' as service name if not set
|
||||
if options.ServiceName == "" {
|
||||
options.ServiceName = "reva"
|
||||
}
|
||||
|
||||
return getOtlpTracerProvider(options)
|
||||
SetDefaultTracerProvider(tp)
|
||||
return tp
|
||||
}
|
||||
|
||||
// SetDefaultTracerProvider sets the default trace provider
|
||||
@@ -80,21 +90,6 @@ func SetDefaultTracerProvider(tp trace.TracerProvider) {
|
||||
defaultProvider.initialized = true
|
||||
}
|
||||
|
||||
// InitDefaultTracerProvider initializes a global default jaeger TracerProvider at a package level.
|
||||
//
|
||||
// Deprecated: Use NewTracerProvider and SetDefaultTracerProvider to properly initialize a tracer provider with options
|
||||
func InitDefaultTracerProvider(collector, endpoint string) {
|
||||
defaultProvider.mutex.Lock()
|
||||
defer defaultProvider.mutex.Unlock()
|
||||
if !defaultProvider.initialized {
|
||||
SetDefaultTracerProvider(getOtlpTracerProvider(Options{
|
||||
Endpoint: endpoint,
|
||||
ServiceName: "reva default otlp provider",
|
||||
Insecure: true,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultProvider returns the "global" default TracerProvider
|
||||
// Currently used by the pool to get the global tracer
|
||||
func DefaultProvider() trace.TracerProvider {
|
||||
@@ -102,42 +97,3 @@ func DefaultProvider() trace.TracerProvider {
|
||||
defer defaultProvider.mutex.RUnlock()
|
||||
return otel.GetTracerProvider()
|
||||
}
|
||||
|
||||
// getOtelTracerProvider returns a new TracerProvider, configure for the specified service
|
||||
func getOtlpTracerProvider(options Options) trace.TracerProvider {
|
||||
transportCredentials := options.TransportCredentials
|
||||
if options.Insecure {
|
||||
transportCredentials = insecure.NewCredentials()
|
||||
}
|
||||
conn, err := grpc.NewClient(options.Endpoint,
|
||||
grpc.WithTransportCredentials(transportCredentials),
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create gRPC connection to endpoint: %w", err))
|
||||
}
|
||||
exporter, err := otlptracegrpc.New(
|
||||
context.Background(),
|
||||
otlptracegrpc.WithGRPCConn(conn),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resources, err := resource.New(
|
||||
context.Background(),
|
||||
resource.WithAttributes(
|
||||
attribute.String("service.name", options.ServiceName),
|
||||
attribute.String("library.language", "go"),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return sdktrace.NewTracerProvider(
|
||||
sdktrace.WithSampler(sdktrace.AlwaysSample()),
|
||||
sdktrace.WithBatcher(exporter),
|
||||
sdktrace.WithResource(resources),
|
||||
)
|
||||
}
|
||||
|
||||
7
vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go
generated
vendored
7
vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go
generated
vendored
@@ -22,6 +22,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
@@ -270,7 +271,11 @@ func (m *manager) ldapEntryToUser(entry *ldap.Entry) (*userpb.User, error) {
|
||||
func (m *manager) ldapEntryToUserID(entry *ldap.Entry) (*userpb.UserId, error) {
|
||||
var uid string
|
||||
if m.c.LDAPIdentity.User.Schema.IDIsOctetString {
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(m.c.LDAPIdentity.User.Schema.ID)
|
||||
attribute := m.c.LDAPIdentity.User.Schema.ID
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(attribute)
|
||||
if strings.EqualFold(attribute, "objectguid") {
|
||||
rawValue = ldapIdentity.SwapObjectGUIDBytes(rawValue)
|
||||
}
|
||||
if value, err := uuid.FromBytes(rawValue); err == nil {
|
||||
uid = value.String()
|
||||
} else {
|
||||
|
||||
57
vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go
generated
vendored
57
vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go
generated
vendored
@@ -405,15 +405,19 @@ func (i *Identity) GetLDAPUserGroups(ctx context.Context, lc ldap.Client, userEn
|
||||
// FIXME 1. use the memberof or members attribute of a user to get the groups
|
||||
// FIXME 2. ook up the id for each group
|
||||
var groupID string
|
||||
attribute := i.Group.Schema.ID
|
||||
if i.Group.Schema.IDIsOctetString {
|
||||
raw := entry.GetEqualFoldRawAttributeValue(i.Group.Schema.ID)
|
||||
value, err := uuid.FromBytes(raw)
|
||||
rawValue := entry.GetEqualFoldRawAttributeValue(attribute)
|
||||
if strings.EqualFold(attribute, "objectguid") {
|
||||
rawValue = SwapObjectGUIDBytes(rawValue)
|
||||
}
|
||||
value, err := uuid.FromBytes(rawValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupID = value.String()
|
||||
} else {
|
||||
groupID = entry.GetEqualFoldAttributeValue(i.Group.Schema.ID)
|
||||
groupID = entry.GetEqualFoldAttributeValue(attribute)
|
||||
}
|
||||
|
||||
groups = append(groups, groupID)
|
||||
@@ -556,17 +560,26 @@ func filterEscapeAttribute(attribute string, binary bool, id string) (string, er
|
||||
return escaped, nil
|
||||
}
|
||||
|
||||
// swapObjectGUIDBytes converts between AD's mixed-endian objectGUID format and standard UUID byte order
|
||||
// AD stores objectGUID with mixed endianness 🤪 - swap first 3 components
|
||||
func SwapObjectGUIDBytes(value []byte) []byte {
|
||||
if len(value) != 16 {
|
||||
return value
|
||||
}
|
||||
return []byte{
|
||||
value[3], value[2], value[1], value[0], // First component (4 bytes) - reverse
|
||||
value[5], value[4], // Second component (2 bytes) - reverse
|
||||
value[7], value[6], // Third component (2 bytes) - reverse
|
||||
value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15], // Last 8 bytes - keep as-is
|
||||
}
|
||||
}
|
||||
|
||||
func filterEscapeBinaryUUID(attribute string, value uuid.UUID) string {
|
||||
bytes := value[:]
|
||||
|
||||
// AD stores objectGUID with mixed endianness 🤪 - swap first 3 components
|
||||
if strings.EqualFold(attribute, "objectguid") {
|
||||
bytes = []byte{
|
||||
value[3], value[2], value[1], value[0], // First component (4 bytes) - reverse
|
||||
value[5], value[4], // Second component (2 bytes) - reverse
|
||||
value[7], value[6], // Third component (2 bytes) - reverse
|
||||
value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15], // Last 8 bytes - keep as-is
|
||||
}
|
||||
bytes = SwapObjectGUIDBytes(bytes)
|
||||
}
|
||||
|
||||
var filtered strings.Builder
|
||||
@@ -656,10 +669,18 @@ func (i *Identity) getUserFindFilter(query, tenantID string) string {
|
||||
for _, attr := range searchAttrs {
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, attr, squery)
|
||||
}
|
||||
// substring search for UUID is not possible
|
||||
filter = fmt.Sprintf("(|%s(%s=%s))", filter, i.User.Schema.ID, ldap.EscapeFilter(query))
|
||||
if i.Group.Schema.IDIsOctetString {
|
||||
// try parsing query as uuid
|
||||
idFilter, err := filterEscapeAttribute(i.User.Schema.ID, i.User.Schema.IDIsOctetString, query)
|
||||
if err == nil {
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, i.User.Schema.ID, idFilter)
|
||||
}
|
||||
} else {
|
||||
// substring search for UUID is not possible
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, i.User.Schema.ID, ldap.EscapeFilter(query))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("(&%s(objectclass=%s)%s%s)",
|
||||
return fmt.Sprintf("(&%s(objectclass=%s)%s(|%s))",
|
||||
i.User.Filter,
|
||||
i.User.Objectclass,
|
||||
i.tenantFilter(tenantID),
|
||||
@@ -687,8 +708,16 @@ func (i *Identity) getGroupFindFilter(query string) string {
|
||||
for _, attr := range searchAttrs {
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, attr, squery)
|
||||
}
|
||||
// substring search for UUID is not possible
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, i.Group.Schema.ID, ldap.EscapeFilter(query))
|
||||
if i.Group.Schema.IDIsOctetString {
|
||||
// try parsing query as uuid
|
||||
idFilter, err := filterEscapeAttribute(i.Group.Schema.ID, i.Group.Schema.IDIsOctetString, query)
|
||||
if err == nil {
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, i.Group.Schema.ID, idFilter)
|
||||
}
|
||||
} else {
|
||||
// substring search for UUID is not possible
|
||||
filter = fmt.Sprintf("%s(%s=%s)", filter, i.Group.Schema.ID, ldap.EscapeFilter(query))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("(&%s(objectclass=%s)(|%s))",
|
||||
i.Group.Filter,
|
||||
|
||||
2
vendor/go.etcd.io/etcd/api/v3/version/version.go
generated
vendored
2
vendor/go.etcd.io/etcd/api/v3/version/version.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
var (
|
||||
// MinClusterVersion is the min cluster version this etcd binary is compatible with.
|
||||
MinClusterVersion = "3.0.0"
|
||||
Version = "3.6.5"
|
||||
Version = "3.6.6"
|
||||
APIVersion = "unknown"
|
||||
|
||||
// Git SHA Value will be set during build
|
||||
|
||||
8
vendor/modules.txt
vendored
8
vendor/modules.txt
vendored
@@ -1370,7 +1370,7 @@ github.com/opencloud-eu/icap-client
|
||||
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
|
||||
## explicit; go 1.18
|
||||
github.com/opencloud-eu/libre-graph-api-go
|
||||
# github.com/opencloud-eu/reva/v2 v2.39.3
|
||||
# github.com/opencloud-eu/reva/v2 v2.40.1
|
||||
## explicit; go 1.24.1
|
||||
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
|
||||
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
|
||||
@@ -2246,7 +2246,7 @@ go.etcd.io/bbolt
|
||||
go.etcd.io/bbolt/errors
|
||||
go.etcd.io/bbolt/internal/common
|
||||
go.etcd.io/bbolt/internal/freelist
|
||||
# go.etcd.io/etcd/api/v3 v3.6.5
|
||||
# go.etcd.io/etcd/api/v3 v3.6.6
|
||||
## explicit; go 1.24
|
||||
go.etcd.io/etcd/api/v3/authpb
|
||||
go.etcd.io/etcd/api/v3/etcdserverpb
|
||||
@@ -2255,7 +2255,7 @@ go.etcd.io/etcd/api/v3/mvccpb
|
||||
go.etcd.io/etcd/api/v3/v3rpc/rpctypes
|
||||
go.etcd.io/etcd/api/v3/version
|
||||
go.etcd.io/etcd/api/v3/versionpb
|
||||
# go.etcd.io/etcd/client/pkg/v3 v3.6.5
|
||||
# go.etcd.io/etcd/client/pkg/v3 v3.6.6
|
||||
## explicit; go 1.24
|
||||
go.etcd.io/etcd/client/pkg/v3/fileutil
|
||||
go.etcd.io/etcd/client/pkg/v3/logutil
|
||||
@@ -2264,7 +2264,7 @@ go.etcd.io/etcd/client/pkg/v3/tlsutil
|
||||
go.etcd.io/etcd/client/pkg/v3/transport
|
||||
go.etcd.io/etcd/client/pkg/v3/types
|
||||
go.etcd.io/etcd/client/pkg/v3/verify
|
||||
# go.etcd.io/etcd/client/v3 v3.6.5
|
||||
# go.etcd.io/etcd/client/v3 v3.6.6
|
||||
## explicit; go 1.24
|
||||
go.etcd.io/etcd/client/v3
|
||||
go.etcd.io/etcd/client/v3/credentials
|
||||
|
||||
Reference in New Issue
Block a user