Bump reva to latest main

to get https://github.com/opencloud-eu/reva/pull/339
This commit is contained in:
Ralf Haferkamp
2025-09-09 10:30:25 +02:00
committed by Ralf Haferkamp
parent a5e0c1ec4b
commit 65228f3188
27 changed files with 388 additions and 148 deletions

12
go.mod
View File

@@ -65,7 +65,7 @@ require (
github.com/onsi/gomega v1.38.2
github.com/open-policy-agent/opa v1.6.0
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
github.com/opencloud-eu/reva/v2 v2.37.0
github.com/opencloud-eu/reva/v2 v2.37.1-0.20250909074412-f272eeaa2674
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
@@ -99,7 +99,7 @@ require (
go.opentelemetry.io/contrib/zpages v0.62.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto v0.41.0
@@ -110,7 +110,7 @@ require (
golang.org/x/sync v0.17.0
golang.org/x/term v0.34.0
golang.org/x/text v0.28.0
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8
gopkg.in/yaml.v2 v2.4.0
@@ -159,7 +159,7 @@ require (
github.com/bluele/gcache v0.0.2 // indirect
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/ceph/go-ceph v0.35.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
@@ -354,9 +354,9 @@ require (
go.etcd.io/etcd/client/v3 v3.6.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect

24
go.sum
View File

@@ -206,8 +206,8 @@ github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QH
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/ceph/go-ceph v0.35.0 h1:wcDUbsjeNJ7OfbWCE7I5prqUL794uXchopw3IvrGQkk=
@@ -916,8 +916,8 @@ github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-202505121527
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
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.37.0 h1:PKX425FaLlA7Zd5VG6XbHKdBoapJGFv/vc9+njJr/hs=
github.com/opencloud-eu/reva/v2 v2.37.0/go.mod h1:aNiCrmC5jZJ/Nxqn1UaqyOTez1S7mDLWcpqPbPNYI6A=
github.com/opencloud-eu/reva/v2 v2.37.1-0.20250909074412-f272eeaa2674 h1:35OSsH9o4GAL9LX+BckhYlpp+EqJ0Bz69MXz+5gpaV4=
github.com/opencloud-eu/reva/v2 v2.37.1-0.20250909074412-f272eeaa2674/go.mod h1:eu0fTK68n+drdu7eT0u1QODDH3VHMugAdrS6G5VhMwA=
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=
@@ -1250,10 +1250,10 @@ go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
@@ -1264,8 +1264,8 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1677,8 +1677,8 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c h1:AtEkQdl5b6zsybXcbz00j1LwNodDuH6hVifIaNqk7NQ=
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c/go.mod h1:ea2MjsO70ssTfCjiwHgI0ZFqcw45Ksuk2ckf9G468GA=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=

View File

@@ -1,7 +1,7 @@
package backoff
import (
"math/rand"
"math/rand/v2"
"time"
)
@@ -28,13 +28,7 @@ multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Example: Given the following default arguments, for 9 tries the sequence will be:
Request # RetryInterval (seconds) Randomized Interval (seconds)
@@ -47,7 +41,6 @@ and assuming we go over the MaxElapsedTime on the 10th try:
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/

View File

@@ -47,7 +47,7 @@ func WithNotify(n Notify) RetryOption {
}
}
// WithMaxTries limits the number of retry attempts.
// WithMaxTries limits the number of all attempts.
func WithMaxTries(n uint) RetryOption {
return func(args *retryOptions) {
args.MaxTries = n
@@ -97,7 +97,7 @@ func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOpti
// Handle permanent errors without retrying.
var permanent *PermanentError
if errors.As(err, &permanent) {
return res, err
return res, permanent.Unwrap()
}
// Stop retrying if context is cancelled.

View File

@@ -136,6 +136,11 @@ func (s *service) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*use
return res, nil
}
// Only request users from the same tenant as the current user
if currentUser, ok := revactx.ContextGetUser(ctx); ok {
req.UserId.TenantId = currentUser.GetId().GetTenantId()
}
user, err := s.usermgr.GetUser(ctx, req.UserId, req.SkipFetchingUserGroups)
if err != nil {
res := &userpb.GetUserResponse{}
@@ -155,7 +160,11 @@ func (s *service) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*use
}
func (s *service) GetUserByClaim(ctx context.Context, req *userpb.GetUserByClaimRequest) (*userpb.GetUserByClaimResponse, error) {
user, err := s.usermgr.GetUserByClaim(ctx, req.Claim, req.Value, req.SkipFetchingUserGroups)
tenantID := ""
if currentUser, ok := revactx.ContextGetUser(ctx); ok {
tenantID = currentUser.GetId().GetTenantId()
}
user, err := s.usermgr.GetUserByClaim(ctx, req.Claim, req.Value, tenantID, req.SkipFetchingUserGroups)
if err != nil {
res := &userpb.GetUserByClaimResponse{}
if _, ok := err.(errtypes.NotFound); ok {
@@ -176,7 +185,7 @@ func (s *service) GetUserByClaim(ctx context.Context, req *userpb.GetUserByClaim
func (s *service) FindUsers(ctx context.Context, req *userpb.FindUsersRequest) (*userpb.FindUsersResponse, error) {
currentUser := revactx.ContextMustGetUser(ctx)
users, err := s.usermgr.FindUsers(ctx, req.Filter, currentUser.Id.GetTenantId(), req.SkipFetchingUserGroups)
users, err := s.usermgr.FindUsers(ctx, req.Filter, currentUser.GetId().GetTenantId(), req.SkipFetchingUserGroups)
if err != nil {
res := &userpb.FindUsersResponse{
Status: status.NewInternal(ctx, "error finding users"),

View File

@@ -108,7 +108,7 @@ func (am *mgr) Authenticate(ctx context.Context, clientID, clientSecret string)
filter := am.getLoginFilter(clientID)
userEntry, err := am.c.LDAPIdentity.GetLDAPUserByFilter(log, am.ldapClient, filter)
userEntry, err := am.c.LDAPIdentity.GetLDAPUserByFilter(ctx, am.ldapClient, filter)
if err != nil {
return nil, nil, err

View File

@@ -224,6 +224,9 @@ func (m *manager) parseAndCacheUser(ctx context.Context, userData map[string]int
}
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if uid.GetTenantId() != "" {
return nil, errtypes.NotSupported("tenant filter not supported in rest user manager")
}
u, err := m.fetchCachedUserDetails(uid)
if err != nil {
return nil, err
@@ -240,7 +243,10 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return u, nil
}
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
if tenantID != "" {
return nil, errtypes.NotSupported("tenant filter not supported in rest user manager")
}
u, err := m.fetchCachedUserByParam(claim, value)
if err != nil {
return nil, err

View File

@@ -35,6 +35,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/utils"
ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/attribute"
)
func init() {
@@ -54,6 +55,8 @@ type config struct {
Nobody int64 `mapstructure:"nobody"`
}
const tracerName = "pkg/group/manager/ldap"
func parseConfig(m map[string]interface{}) (*config, error) {
c := config{
LDAPIdentity: ldapIdentity.New(),
@@ -100,12 +103,20 @@ func (m *manager) Configure(ml map[string]interface{}) error {
// GetGroup implements the group.Manager interface. Looks up a group by Id and return the group
func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId, skipFetchingMembers bool) (*grouppb.Group, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetGroup")
defer span.End()
span.SetAttributes(
attribute.Stringer("group_id", gid),
attribute.Bool("skip_fetching_members", skipFetchingMembers),
)
log := appctx.GetLogger(ctx)
if gid.Idp != "" && gid.Idp != m.c.Idp {
return nil, errtypes.NotFound("idp mismatch")
}
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByID(log, m.ldapClient, gid.OpaqueId)
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByID(ctx, m.ldapClient, gid.OpaqueId)
if err != nil {
return nil, err
}
@@ -121,7 +132,7 @@ func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId, skipFetchi
return g, nil
}
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(log, m.ldapClient, groupEntry)
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(ctx, m.ldapClient, groupEntry)
if err != nil {
return nil, err
}
@@ -144,8 +155,17 @@ func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId, skipFetchi
// GetGroupByClaim implements the group.Manager interface. Looks up a group by
// claim ('group_name', 'group_id', 'display_name') and returns the group.
func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string, skipFetchingMembers bool) (*grouppb.Group, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetGroupByClaim")
defer span.End()
span.SetAttributes(
attribute.String("claim", claim),
attribute.String("value", value),
attribute.Bool("skip_fetching_members", skipFetchingMembers),
)
log := appctx.GetLogger(ctx)
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByAttribute(log, m.ldapClient, claim, value)
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByAttribute(ctx, m.ldapClient, claim, value)
if err != nil {
log.Debug().Err(err).Msg("GetGroupByClaim")
return nil, err
@@ -162,7 +182,7 @@ func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string, skip
return g, nil
}
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(log, m.ldapClient, groupEntry)
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(ctx, m.ldapClient, groupEntry)
if err != nil {
return nil, err
}
@@ -187,8 +207,15 @@ func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string, skip
// 'display_name', 'group_id') and returns the groups. FindGroups does NOT expand the
// members of the Groups.
func (m *manager) FindGroups(ctx context.Context, query string, skipFetchingMembers bool) ([]*grouppb.Group, error) {
log := appctx.GetLogger(ctx)
entries, err := m.c.LDAPIdentity.GetLDAPGroups(log, m.ldapClient, query)
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "FindGroups")
defer span.End()
span.SetAttributes(
attribute.String("query", query),
attribute.Bool("skip_fetching_members", skipFetchingMembers),
)
entries, err := m.c.LDAPIdentity.GetLDAPGroups(ctx, m.ldapClient, query)
if err != nil {
return nil, err
}
@@ -210,19 +237,25 @@ func (m *manager) FindGroups(ctx context.Context, query string, skipFetchingMemb
// GetMembers implements the group.Manager interface. It returns all the userids of the members
// of the group identified by the supplied id.
func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*userpb.UserId, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetMembers")
defer span.End()
span.SetAttributes(
attribute.Stringer("group_id", gid),
)
log := appctx.GetLogger(ctx)
if gid.Idp != "" && gid.Idp != m.c.Idp {
return nil, errtypes.NotFound("idp mismatch")
}
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByID(log, m.ldapClient, gid.OpaqueId)
groupEntry, err := m.c.LDAPIdentity.GetLDAPGroupByID(ctx, m.ldapClient, gid.OpaqueId)
if err != nil {
return nil, err
}
log.Debug().Interface("entry", groupEntry).Msg("entries")
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(log, m.ldapClient, groupEntry)
members, err := m.c.LDAPIdentity.GetLDAPGroupMembers(ctx, m.ldapClient, groupEntry)
if err != nil {
return nil, err
}
@@ -243,6 +276,13 @@ func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*user
// HasMember implements the group.Member interface. Checks whether the supplied userid is a member
// of the supplied groupid.
func (m *manager) HasMember(ctx context.Context, gid *grouppb.GroupId, uid *userpb.UserId) (bool, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "HasMember")
defer span.End()
span.SetAttributes(
attribute.Stringer("group_id", gid),
attribute.Stringer("user_id", uid),
)
// It might be possible to do a somewhat more clever LDAP search here. (First lookup the user and then
// search for (&(objectclass=<groupoc>)(<groupid>=gid)(member=<username/userdn>)
// The GetMembers call used below can be quiet ineffecient for large groups

View File

@@ -57,7 +57,7 @@ func (m *manager) Configure(ml map[string]interface{}) error {
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if user, ok := m.catalog[uid.OpaqueId]; ok {
if uid.Idp == "" || user.Id.Idp == uid.Idp {
if user.GetId().GetTenantId() == uid.GetTenantId() && (uid.Idp == "" || user.Id.Idp == uid.Idp) {
u := proto.Clone(user).(*userpb.User)
if skipFetchingGroups {
u.Groups = nil
@@ -68,9 +68,9 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return nil, errtypes.NotFound(uid.OpaqueId)
}
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
for _, u := range m.catalog {
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim && tenantID == u.Id.TenantId {
user := proto.Clone(u).(*userpb.User)
if skipFetchingGroups {
user.Groups = nil

View File

@@ -97,7 +97,7 @@ func (m *manager) Configure(ml map[string]interface{}) error {
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
for _, u := range m.users {
if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) {
if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) && (uid.GetTenantId() == u.Id.GetTenantId()) {
user := proto.Clone(u).(*userpb.User)
if skipFetchingGroups {
user.Groups = nil
@@ -108,9 +108,9 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return nil, errtypes.NotFound(uid.OpaqueId)
}
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
for _, u := range m.users {
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim && tenantID == u.Id.TenantId {
user := proto.Clone(u).(*userpb.User)
if skipFetchingGroups {
user.Groups = nil

View File

@@ -34,12 +34,15 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/utils"
ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/attribute"
)
func init() {
registry.Register("ldap", New)
}
const tracerName = "pkg/user/manager/ldap"
type manager struct {
c *config
ldapClient ldap.Client
@@ -96,15 +99,22 @@ func (m *manager) Configure(ml map[string]interface{}) error {
// GetUser implements the user.Manager interface. Looks up a user by Id and return the user
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
log := appctx.GetLogger(ctx)
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetUser")
defer span.End()
span.SetAttributes(
attribute.Stringer("parameter.userid", uid),
attribute.Bool("parameter.skipFetchingGroups", skipFetchingGroups),
)
log := appctx.GetLogger(ctx)
log.Debug().Interface("id", uid).Msg("GetUser")
// If the Idp value in the uid does not match our config, we can't answer this request
if uid.Idp != "" && uid.Idp != m.c.Idp {
return nil, errtypes.NotFound("idp mismatch")
}
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId)
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(ctx, m.ldapClient, uid)
if err != nil {
return nil, err
}
@@ -120,7 +130,7 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return u, nil
}
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(ctx, m.ldapClient, userEntry)
if err != nil {
return nil, err
}
@@ -131,11 +141,18 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
// GetUserByClaim implements the user.Manager interface. Looks up a user by
// claim ('mail', 'username', 'userid') and returns the user.
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
log := appctx.GetLogger(ctx)
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetUserByClaim")
defer span.End()
span.SetAttributes(
attribute.String("parameter.claim", claim),
attribute.String("paramter.value", value),
attribute.Bool("parameter.skipFetchingGroups", skipFetchingGroups),
)
log.Debug().Str("claim", claim).Str("value", value).Msg("GetUserByClaim")
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByAttribute(log, m.ldapClient, claim, value)
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByAttribute(ctx, m.ldapClient, claim, value, tenantID)
if err != nil {
log.Debug().Err(err).Msg("GetUserByClaim")
return nil, err
@@ -148,7 +165,7 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF
return nil, err
}
if m.c.LDAPIdentity.IsLDAPUserInDisabledGroup(log, m.ldapClient, userEntry) {
if m.c.LDAPIdentity.IsLDAPUserInDisabledGroup(ctx, m.ldapClient, userEntry) {
return nil, errtypes.NotFound("user is locally disabled")
}
@@ -156,7 +173,7 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF
return u, nil
}
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(ctx, m.ldapClient, userEntry)
if err != nil {
return nil, err
}
@@ -169,8 +186,15 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF
// FindUser implements the user.Manager interface. Searches for users using a prefix-substring search on
// the user attributes ('mail', 'username', 'displayname', 'userid') and returns the users.
func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) {
log := appctx.GetLogger(ctx)
entries, err := m.c.LDAPIdentity.GetLDAPUsers(log, m.ldapClient, query, tenantID)
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "FindUsers")
defer span.End()
span.SetAttributes(
attribute.String("parameter.query", query),
attribute.String("parameter.tenantID", tenantID),
attribute.Bool("parameter.skipFetchingGroups", skipFetchingGroups),
)
entries, err := m.c.LDAPIdentity.GetLDAPUsers(ctx, m.ldapClient, query, tenantID)
if err != nil {
return nil, err
}
@@ -183,7 +207,7 @@ func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFet
}
if !skipFetchingGroups {
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, entry)
groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(ctx, m.ldapClient, entry)
if err != nil {
return nil, err
}
@@ -204,12 +228,12 @@ func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]stri
log.Debug().Str("useridp", uid.Idp).Str("configured idp", m.c.Idp).Msg("IDP mismatch")
return nil, errtypes.NotFound("idp mismatch")
}
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId)
userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(ctx, m.ldapClient, uid)
if err != nil {
log.Debug().Err(err).Interface("userid", uid).Msg("Failed to lookup user")
return []string{}, err
}
return m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry)
return m.c.LDAPIdentity.GetLDAPUserGroups(ctx, m.ldapClient, userEntry)
}
func (m *manager) ldapEntryToUser(entry *ldap.Entry) (*userpb.User, error) {

View File

@@ -84,6 +84,9 @@ func (m *manager) Configure(ml map[string]interface{}) error {
}
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if uid.GetTenantId() != "" {
return nil, errtypes.NotSupported("tenant filter not supported in memory user manager")
}
if user, ok := m.catalog[uid.OpaqueId]; ok {
if uid.Idp == "" || user.ID.Idp == uid.Idp {
u := *user
@@ -106,7 +109,11 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return nil, errtypes.NotFound(uid.OpaqueId)
}
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
if tenantID != "" {
return nil, errtypes.NotSupported("tenant filter not supported in memory user manager")
}
for _, u := range m.catalog {
if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
user := &userpb.User{

View File

@@ -151,6 +151,9 @@ func (um *Manager) Configure(ml map[string]interface{}) error {
// GetUser method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
func (um *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if uid.GetTenantId() != "" {
return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager")
}
bodyStr, _ := json.Marshal(uid)
_, respBody, err := um.do(ctx, Action{"GetUser", string(bodyStr)}, "unauthenticated")
if err != nil {
@@ -165,7 +168,10 @@ func (um *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetching
}
// GetUserByClaim method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
func (um *Manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (um *Manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
if tenantID != "" {
return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager")
}
type paramsObj struct {
Claim string `json:"claim"`
Value string `json:"value"`

View File

@@ -103,6 +103,9 @@ func parseConfig(m map[string]interface{}) (*config, error) {
}
func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if uid.GetTenantId() != "" {
return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager")
}
// search via the user_id
a, err := m.db.GetAccountByClaim(ctx, "userid", uid.OpaqueId)
if err == sql.ErrNoRows {
@@ -111,7 +114,11 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingG
return m.convertToCS3User(ctx, a, skipFetchingGroups)
}
func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
if tenantID != "" {
return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager")
}
a, err := m.db.GetAccountByClaim(ctx, claim, value)
if err == sql.ErrNoRows {
return nil, errtypes.NotFound(claim + "=" + value)

View File

@@ -89,6 +89,9 @@ type GetUserReply struct {
// GetUser RPCClient GetUser method
func (m *RPCClient) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
if uid.GetTenantId() != "" {
return nil, errtypes.NotSupported("tenant filter not supported in rpc_user user manager")
}
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
args := GetUserArg{Ctx: ctxVal, UID: uid, SkipFetchingGroups: skipFetchingGroups}
resp := GetUserReply{}
@@ -114,7 +117,11 @@ type GetUserByClaimReply struct {
}
// GetUserByClaim RPCClient GetUserByClaim method
func (m *RPCClient) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
func (m *RPCClient) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
if tenantID != "" {
return nil, errtypes.NotSupported("tenant filter not supported in rpc_user user manager")
}
ctxVal := appctx.GetKeyValuesFromCtx(ctx)
args := GetUserByClaimArg{Ctx: ctxVal, Claim: claim, Value: value, SkipFetchingGroups: skipFetchingGroups}
resp := GetUserByClaimReply{}
@@ -200,7 +207,7 @@ func (m *RPCServer) GetUser(args GetUserArg, resp *GetUserReply) error {
// GetUserByClaim RPCServer GetUserByClaim method
func (m *RPCServer) GetUserByClaim(args GetUserByClaimArg, resp *GetUserByClaimReply) error {
ctx := appctx.PutKeyValuesToCtx(args.Ctx)
resp.User, resp.Err = m.Impl.GetUserByClaim(ctx, args.Claim, args.Value, args.SkipFetchingGroups)
resp.User, resp.Err = m.Impl.GetUserByClaim(ctx, args.Claim, args.Value, "", args.SkipFetchingGroups)
return nil
}

View File

@@ -33,7 +33,7 @@ type Manager interface {
// and might involve computational overhead.
GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error)
// GetUserByClaim returns the user identified by a specific value for a given claim.
GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error)
GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error)
// GetUserGroups returns the groups a user identified by a uid belongs to.
GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error)
// FindUsers returns all the user objects which match a query parameter.

View File

@@ -19,15 +19,20 @@
package ldap
import (
"context"
"fmt"
"strings"
identityUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/go-ldap/ldap/v3"
"github.com/google/uuid"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)
// Identity provides methods to query users and groups from an LDAP server
@@ -36,6 +41,8 @@ type Identity struct {
Group groupConfig `mapstructure:",squash"`
}
const tracerName = "pkg/utils/ldap"
type userConfig struct {
BaseDN string `mapstructure:"user_base_dn"`
Scope string `mapstructure:"user_search_scope"`
@@ -178,45 +185,40 @@ func (i *Identity) Setup() error {
// GetLDAPUserByID looks up a user by the supplied Id. Returns the corresponding
// ldap.Entry
func (i *Identity) GetLDAPUserByID(log *zerolog.Logger, lc ldap.Client, id string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPUserByID(ctx context.Context, lc ldap.Client, id *identityUser.UserId) (*ldap.Entry, error) {
var filter string
var err error
if filter, err = i.getUserFilter(id); err != nil {
return nil, err
}
return i.GetLDAPUserByFilter(log, lc, filter)
return i.GetLDAPUserByFilter(ctx, lc, filter)
}
// GetLDAPUserByAttribute looks up a single user by attribute (can be "mail",
// "uid", "gid", "username" or "userid"). Returns the corresponding ldap.Entry
func (i *Identity) GetLDAPUserByAttribute(log *zerolog.Logger, lc ldap.Client, attribute, value string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPUserByAttribute(ctx context.Context, lc ldap.Client, attribute, value, tenantID string) (*ldap.Entry, error) {
var filter string
var err error
if filter, err = i.getUserAttributeFilter(attribute, value); err != nil {
if filter, err = i.getUserAttributeFilter(attribute, value, tenantID); err != nil {
return nil, err
}
return i.GetLDAPUserByFilter(log, lc, filter)
return i.GetLDAPUserByFilter(ctx, lc, filter)
}
// GetLDAPUserByFilter looks up a single user by the supplied LDAP filter
// returns the corresponding ldap.Entry
func (i *Identity) GetLDAPUserByFilter(log *zerolog.Logger, lc ldap.Client, filter string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPUserByFilter(ctx context.Context, lc ldap.Client, filter string) (*ldap.Entry, error) {
log := appctx.GetLogger(ctx)
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUserByFilter")
defer span.End()
searchRequest := ldap.NewSearchRequest(
i.User.BaseDN, i.User.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
filter,
[]string{
i.User.Schema.DisplayName,
i.User.Schema.ID,
i.User.Schema.TenantID,
i.User.Schema.Mail,
i.User.Schema.Username,
i.User.Schema.UIDNumber,
i.User.Schema.GIDNumber,
i.User.EnabledProperty,
i.User.UserTypeProperty,
},
i.getUserLDAPAttrTypes(),
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
log.Debug().Str("backend", "ldap").Str("basedn", i.User.BaseDN).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search")
res, err := lc.Search(searchRequest)
if err != nil {
@@ -227,18 +229,25 @@ func (i *Identity) GetLDAPUserByFilter(log *zerolog.Logger, lc ldap.Client, filt
errmsg = fmt.Sprintf("too many results searching for user '%s'", filter)
}
}
span.SetAttributes(attribute.String("ldap.error", errmsg))
span.SetStatus(codes.Error, errmsg)
return nil, errtypes.NotFound(errmsg)
}
if len(res.Entries) == 0 {
return nil, errtypes.NotFound(filter)
}
span.SetStatus(codes.Ok, "")
return res.Entries[0], nil
}
// GetLDAPUserByDN looks up a single user by the supplied LDAP DN
// returns the corresponding ldap.Entry
func (i *Identity) GetLDAPUserByDN(log *zerolog.Logger, lc ldap.Client, dn string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPUserByDN(ctx context.Context, lc ldap.Client, dn string) (*ldap.Entry, error) {
log := appctx.GetLogger(ctx)
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUserByDN")
defer span.End()
filter := fmt.Sprintf("(objectclass=%s)", i.User.Objectclass)
if i.User.Filter != "" {
filter = fmt.Sprintf("(&%s%s)", i.User.Filter, filter)
@@ -246,23 +255,19 @@ func (i *Identity) GetLDAPUserByDN(log *zerolog.Logger, lc ldap.Client, dn strin
searchRequest := ldap.NewSearchRequest(
dn, i.User.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
filter,
[]string{
i.User.Schema.DisplayName,
i.User.Schema.ID,
i.User.Schema.Mail,
i.User.Schema.Username,
i.User.Schema.UIDNumber,
i.User.Schema.GIDNumber,
i.User.EnabledProperty,
},
i.getUserLDAPAttrTypes(),
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
log.Debug().Str("backend", "ldap").Str("basedn", dn).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search")
res, err := lc.Search(searchRequest)
if err != nil {
log.Debug().Str("backend", "ldap").Err(err).Str("dn", dn).Msg("Error looking up user by DN")
span.SetAttributes(attribute.String("ldap.error", err.Error()))
span.SetStatus(codes.Error, "")
return nil, errtypes.NotFound(dn)
}
span.SetStatus(codes.Ok, "")
if len(res.Entries) == 0 {
return nil, errtypes.NotFound(dn)
}
@@ -272,36 +277,42 @@ func (i *Identity) GetLDAPUserByDN(log *zerolog.Logger, lc ldap.Client, dn strin
// GetLDAPUsers searches for users using a prefix-substring match on the user
// attributes. Returns a slice of matching ldap.Entries
func (i *Identity) GetLDAPUsers(log *zerolog.Logger, lc ldap.Client, query, tenantID string) ([]*ldap.Entry, error) {
func (i *Identity) GetLDAPUsers(ctx context.Context, lc ldap.Client, query, tenantID string) ([]*ldap.Entry, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUsers")
defer span.End()
log := appctx.GetLogger(ctx)
filter := i.getUserFindFilter(query, tenantID)
searchRequest := ldap.NewSearchRequest(
i.User.BaseDN,
i.User.scopeVal, ldap.NeverDerefAliases, 0, 0, false,
filter,
[]string{
i.User.Schema.ID,
i.User.Schema.Username,
i.User.Schema.Mail,
i.User.Schema.DisplayName,
i.User.Schema.UIDNumber,
i.User.Schema.GIDNumber,
i.User.EnabledProperty,
i.User.UserTypeProperty,
},
i.getUserLDAPAttrTypes(),
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
log.Debug().Str("backend", "ldap").Str("basedn", i.User.BaseDN).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search")
sr, err := lc.Search(searchRequest)
if err != nil {
log.Debug().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error searching users")
span.SetAttributes(attribute.String("ldap.error", err.Error()))
span.SetStatus(codes.Error, "")
return nil, errtypes.NotFound(query)
}
span.SetAttributes(attribute.Int("ldap.result_count", len(sr.Entries)))
span.SetStatus(codes.Ok, "")
return sr.Entries, nil
}
// IsLDAPUserInDisabledGroup checkes if the user is in the disabled group.
func (i *Identity) IsLDAPUserInDisabledGroup(log *zerolog.Logger, lc ldap.Client, userEntry *ldap.Entry) bool {
func (i *Identity) IsLDAPUserInDisabledGroup(ctx context.Context, lc ldap.Client, userEntry *ldap.Entry) bool {
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "IsLDAPUserInDisabledGroup")
defer span.End()
span.SetAttributes(attribute.String("identity.config.disable_mechanism", i.User.DisableMechanism))
span.SetStatus(codes.Ok, "")
// Check if we need to do this here because the configuration is local to Identity.
if i.User.DisableMechanism != "group" {
return false
@@ -316,11 +327,15 @@ func (i *Identity) IsLDAPUserInDisabledGroup(log *zerolog.Logger, lc ldap.Client
[]string{i.Group.Schema.ID},
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
log.Debug().Str("backend", "ldap").Str("basedn", i.Group.LocalDisabledDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search")
sr, err := lc.Search(searchRequest)
if err != nil {
log.Error().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error looking up error group")
// Err on the side of caution.
span.SetAttributes(attribute.String("ldap.error", err.Error()))
span.SetStatus(codes.Error, "")
return true
}
@@ -329,9 +344,14 @@ func (i *Identity) IsLDAPUserInDisabledGroup(log *zerolog.Logger, lc ldap.Client
// GetLDAPUserGroups looks up the group member ship of the supplied LDAP user entry.
// Returns a slice of strings with groupids
func (i *Identity) GetLDAPUserGroups(log *zerolog.Logger, lc ldap.Client, userEntry *ldap.Entry) ([]string, error) {
func (i *Identity) GetLDAPUserGroups(ctx context.Context, lc ldap.Client, userEntry *ldap.Entry) ([]string, error) {
var memberValue string
log := appctx.GetLogger(ctx)
_, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPUserGroups")
defer span.End()
if strings.ToLower(i.Group.Objectclass) == "posixgroup" {
// posixGroup usually means that the member attribute just contains the username
memberValue = userEntry.GetEqualFoldAttributeValue(i.User.Schema.Username)
@@ -350,11 +370,16 @@ func (i *Identity) GetLDAPUserGroups(log *zerolog.Logger, lc ldap.Client, userEn
)
log.Debug().Str("backend", "ldap").Str("basedn", i.Group.BaseDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search")
setLDAPSearchSpanAttributes(span, searchRequest)
sr, err := lc.Search(searchRequest)
if err != nil {
log.Debug().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error looking up group memberships")
span.SetAttributes(attribute.String("ldap.error", err.Error()))
span.SetStatus(codes.Error, "")
return []string{}, err
}
span.SetStatus(codes.Ok, "")
span.SetAttributes(attribute.Int("ldap.result_count", len(sr.Entries)))
groups := make([]string, 0, len(sr.Entries))
for _, entry := range sr.Entries {
@@ -380,29 +405,32 @@ func (i *Identity) GetLDAPUserGroups(log *zerolog.Logger, lc ldap.Client, userEn
// GetLDAPGroupByID looks up a group by the supplied Id. Returns the corresponding
// ldap.Entry
func (i *Identity) GetLDAPGroupByID(log *zerolog.Logger, lc ldap.Client, id string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPGroupByID(ctx context.Context, lc ldap.Client, id string) (*ldap.Entry, error) {
var filter string
var err error
if filter, err = i.getGroupFilter(id); err != nil {
return nil, err
}
return i.GetLDAPGroupByFilter(log, lc, filter)
return i.GetLDAPGroupByFilter(ctx, lc, filter)
}
// GetLDAPGroupByAttribute looks up a single group by attribute (can be "mail", "gid_number",
// "display_name", "group_name", "group_id"). Returns the corresponding ldap.Entry
func (i *Identity) GetLDAPGroupByAttribute(log *zerolog.Logger, lc ldap.Client, attribute, value string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPGroupByAttribute(ctx context.Context, lc ldap.Client, attribute, value string) (*ldap.Entry, error) {
var filter string
var err error
if filter, err = i.getGroupAttributeFilter(attribute, value); err != nil {
return nil, err
}
return i.GetLDAPGroupByFilter(log, lc, filter)
return i.GetLDAPGroupByFilter(ctx, lc, filter)
}
// GetLDAPGroupByFilter looks up a single group by the supplied LDAP filter
// returns the corresponding ldap.Entry
func (i *Identity) GetLDAPGroupByFilter(log *zerolog.Logger, lc ldap.Client, filter string) (*ldap.Entry, error) {
func (i *Identity) GetLDAPGroupByFilter(ctx context.Context, lc ldap.Client, filter string) (*ldap.Entry, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPGroupByFilter")
defer span.End()
log := appctx.GetLogger(ctx)
searchRequest := ldap.NewSearchRequest(
i.Group.BaseDN, i.Group.scopeVal, ldap.NeverDerefAliases, 1, 0, false,
filter,
@@ -416,6 +444,7 @@ func (i *Identity) GetLDAPGroupByFilter(log *zerolog.Logger, lc ldap.Client, fil
},
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
log.Debug().Str("backend", "ldap").Str("basedn", i.Group.BaseDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search")
res, err := lc.Search(searchRequest)
@@ -427,18 +456,23 @@ func (i *Identity) GetLDAPGroupByFilter(log *zerolog.Logger, lc ldap.Client, fil
errmsg = fmt.Sprintf("too many results searching for group '%s'", filter)
}
}
span.SetAttributes(attribute.String("ldap.error", errmsg))
span.SetStatus(codes.Error, "")
return nil, errtypes.NotFound(errmsg)
}
if len(res.Entries) == 0 {
return nil, errtypes.NotFound(filter)
}
span.SetStatus(codes.Ok, "")
return res.Entries[0], nil
}
// GetLDAPGroups searches for groups using a prefix-substring match on the group
// attributes. Returns a slice of matching ldap.Entries
func (i *Identity) GetLDAPGroups(log *zerolog.Logger, lc ldap.Client, query string) ([]*ldap.Entry, error) {
func (i *Identity) GetLDAPGroups(ctx context.Context, lc ldap.Client, query string) ([]*ldap.Entry, error) {
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "GetLDAPGroups")
defer span.End()
log := appctx.GetLogger(ctx)
searchRequest := ldap.NewSearchRequest(
i.Group.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
@@ -452,18 +486,22 @@ func (i *Identity) GetLDAPGroups(log *zerolog.Logger, lc ldap.Client, query stri
},
nil,
)
setLDAPSearchSpanAttributes(span, searchRequest)
sr, err := lc.Search(searchRequest)
if err != nil {
span.SetAttributes(attribute.String("ldap.error", err.Error()))
span.SetStatus(codes.Error, "")
log.Debug().Str("backend", "ldap").Err(err).Str("query", query).Msg("Error search for groups")
return nil, errtypes.NotFound(query)
}
span.SetStatus(codes.Ok, "")
return sr.Entries, nil
}
// GetLDAPGroupMembers looks up all members of the supplied LDAP group entry and returns the
// corresponding LDAP user entries
func (i *Identity) GetLDAPGroupMembers(log *zerolog.Logger, lc ldap.Client, group *ldap.Entry) ([]*ldap.Entry, error) {
func (i *Identity) GetLDAPGroupMembers(ctx context.Context, lc ldap.Client, group *ldap.Entry) ([]*ldap.Entry, error) {
log := appctx.GetLogger(ctx)
members := group.GetEqualFoldAttributeValues(i.Group.Schema.Member)
log.Debug().Str("dn", group.DN).Interface("member", members).Msg("Get Group members")
memberEntries := make([]*ldap.Entry, 0, len(members))
@@ -471,9 +509,9 @@ func (i *Identity) GetLDAPGroupMembers(log *zerolog.Logger, lc ldap.Client, grou
var e *ldap.Entry
var err error
if strings.ToLower(i.Group.Objectclass) == "posixgroup" {
e, err = i.GetLDAPUserByAttribute(log, lc, "username", member)
e, err = i.GetLDAPUserByAttribute(ctx, lc, "username", member, "")
} else {
e, err = i.GetLDAPUserByDN(log, lc, member)
e, err = i.GetLDAPUserByDN(ctx, lc, member)
}
if err != nil {
log.Warn().Err(err).Interface("member", member).Msg("Failed read user entry for member")
@@ -493,28 +531,28 @@ func filterEscapeBinaryUUID(value uuid.UUID) string {
return filtered
}
func (i *Identity) getUserFilter(uid string) (string, error) {
func (i *Identity) getUserFilter(uid *identityUser.UserId) (string, error) {
var escapedUUID string
if i.User.Schema.IDIsOctetString {
id, err := uuid.Parse(uid)
id, err := uuid.Parse(uid.GetOpaqueId())
if err != nil {
err := errors.Wrap(err, fmt.Sprintf("error parsing OpaqueID '%s' as UUID", uid))
return "", err
}
escapedUUID = filterEscapeBinaryUUID(id)
} else {
escapedUUID = ldap.EscapeFilter(uid)
escapedUUID = ldap.EscapeFilter(uid.GetOpaqueId())
}
return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s))",
return fmt.Sprintf("(&%s(objectclass=%s)%s(%s=%s))",
i.User.Filter,
i.User.Objectclass,
i.tenantFilter(uid.GetTenantId()),
i.User.Schema.ID,
escapedUUID,
), nil
}
func (i *Identity) getUserAttributeFilter(attribute, value string) (string, error) {
func (i *Identity) getUserAttributeFilter(attribute, value, tenantID string) (string, error) {
switch attribute {
case "mail":
attribute = i.User.Schema.Mail
@@ -541,15 +579,22 @@ func (i *Identity) getUserAttributeFilter(attribute, value string) (string, erro
} else {
value = ldap.EscapeFilter(value)
}
return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s)%s)",
return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s)%s%s)",
i.User.Filter,
i.User.Objectclass,
attribute,
value,
i.tenantFilter(tenantID),
i.disabledFilter(),
), nil
}
func (i *Identity) tenantFilter(tenantID string) string {
if tenantID != "" && i.User.Schema.TenantID != "" {
return fmt.Sprintf("(%s=%s)", i.User.Schema.TenantID, ldap.EscapeFilter(tenantID))
}
return ""
}
func (i *Identity) disabledFilter() string {
if i.User.DisableMechanism == "attribute" {
return fmt.Sprintf("(!(%s=FALSE))", i.User.EnabledProperty)
@@ -580,14 +625,10 @@ func (i *Identity) getUserFindFilter(query, tenantID string) string {
// substring search for UUID is not possible
filter = fmt.Sprintf("(|%s(%s=%s))", filter, i.User.Schema.ID, ldap.EscapeFilter(query))
if tenantID != "" {
// If a tenant ID is provided, we AND a filter for the tenant ID
filter = fmt.Sprintf("(&%s(%s=%s))", filter, i.User.Schema.TenantID, ldap.EscapeFilter(tenantID))
}
return fmt.Sprintf("(&%s(objectclass=%s)%s)",
return fmt.Sprintf("(&%s(objectclass=%s)%s%s)",
i.User.Filter,
i.User.Objectclass,
i.tenantFilter(tenantID),
filter,
)
}
@@ -727,3 +768,40 @@ func (i *Identity) GetUserType(userEntry *ldap.Entry) identityUser.UserType {
return identityUser.UserType_USER_TYPE_PRIMARY
}
}
func (i *Identity) getUserLDAPAttrTypes() []string {
// The are the attributes we request unconditionally when looking up users
// as they are needed to populate a user object
attrs := []string{
i.User.Schema.ID,
i.User.Schema.Username,
i.User.Schema.Mail,
i.User.Schema.DisplayName,
}
// Only add optional attributes if they are configured
if i.User.Schema.UIDNumber != "" {
attrs = append(attrs, i.User.Schema.UIDNumber)
}
if i.User.Schema.GIDNumber != "" {
attrs = append(attrs, i.User.Schema.GIDNumber)
}
if i.User.Schema.TenantID != "" {
attrs = append(attrs, i.User.Schema.TenantID)
}
if i.User.EnabledProperty != "" {
attrs = append(attrs, i.User.EnabledProperty)
}
if i.User.UserTypeProperty != "" {
attrs = append(attrs, i.User.UserTypeProperty)
}
return attrs
}
func setLDAPSearchSpanAttributes(span trace.Span, request *ldap.SearchRequest) {
span.SetAttributes(
attribute.String("ldap.basedn", request.BaseDN),
attribute.String("ldap.filter", request.Filter),
attribute.Int("ldap.scope", request.Scope),
attribute.Int("ldap.size_limit", request.SizeLimit),
)
}

View File

@@ -199,3 +199,33 @@
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.
--------------------------------------------------------------------------------
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -94,7 +94,7 @@ func NewUnstarted(client Client) *Exporter {
}
// MarshalLog is the marshaling function used by the logging system to represent this Exporter.
func (e *Exporter) MarshalLog() interface{} {
func (e *Exporter) MarshalLog() any {
return struct {
Type string
Client Client

View File

@@ -6,9 +6,10 @@
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
import (
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/sdk/resource"
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
)
// KeyValues transforms a slice of attribute KeyValues into OTLP key-values.

View File

@@ -4,8 +4,9 @@
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
import (
"go.opentelemetry.io/otel/sdk/instrumentation"
commonpb "go.opentelemetry.io/proto/otlp/common/v1"
"go.opentelemetry.io/otel/sdk/instrumentation"
)
func InstrumentationScope(il instrumentation.Scope) *commonpb.InstrumentationScope {

View File

@@ -4,8 +4,9 @@
package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform"
import (
"go.opentelemetry.io/otel/sdk/resource"
resourcepb "go.opentelemetry.io/proto/otlp/resource/v1"
"go.opentelemetry.io/otel/sdk/resource"
)
// Resource transforms a Resource into an OTLP Resource.

View File

@@ -6,12 +6,13 @@ package tracetransform // import "go.opentelemetry.io/otel/exporters/otlp/otlptr
import (
"math"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/sdk/instrumentation"
tracesdk "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/trace"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
// Spans transforms a slice of OpenTelemetry spans into a slice of OTLP
@@ -154,7 +155,6 @@ func links(links []tracesdk.Link) []*tracepb.Span_Link {
for _, otLink := range links {
// This redefinition is necessary to prevent otLink.*ID[:] copies
// being reused -- in short we need a new otLink per iteration.
otLink := otLink
tid := otLink.SpanContext.TraceID()
sid := otLink.SpanContext.SpanID()
@@ -189,7 +189,7 @@ func spanEvents(es []tracesdk.Event) []*tracepb.Span_Event {
events := make([]*tracepb.Span_Event, len(es))
// Transform message events
for i := 0; i < len(es); i++ {
for i := range es {
events[i] = &tracepb.Span_Event{
Name: es[i].Name,
TimeUnixNano: uint64(max(0, es[i].Time.UnixNano())), // nolint:gosec // Overflow checked.

View File

@@ -199,3 +199,33 @@
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.
--------------------------------------------------------------------------------
Copyright 2009 The Go Authors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google LLC nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -9,6 +9,8 @@ import (
"sync"
"time"
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
"google.golang.org/genproto/googleapis/rpc/errdetails"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -20,8 +22,6 @@ import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/otlpconfig"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/retry"
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
)
type client struct {
@@ -289,7 +289,7 @@ func throttleDelay(s *status.Status) (bool, time.Duration) {
}
// MarshalLog is the marshaling function used by the logging system to represent this Client.
func (c *client) MarshalLog() interface{} {
func (c *client) MarshalLog() any {
return struct {
Type string
Endpoint string

View File

@@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
func Version() string {
return "1.37.0"
return "1.38.0"
}

12
vendor/modules.txt vendored
View File

@@ -227,7 +227,7 @@ github.com/cenkalti/backoff
# github.com/cenkalti/backoff/v4 v4.3.0
## explicit; go 1.18
github.com/cenkalti/backoff/v4
# github.com/cenkalti/backoff/v5 v5.0.2
# github.com/cenkalti/backoff/v5 v5.0.3
## explicit; go 1.23
github.com/cenkalti/backoff/v5
# github.com/ceph/go-ceph v0.35.0
@@ -1270,7 +1270,7 @@ github.com/open-policy-agent/opa/v1/version
# 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.37.0
# github.com/opencloud-eu/reva/v2 v2.37.1-0.20250909074412-f272eeaa2674
## explicit; go 1.24.1
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -2208,11 +2208,11 @@ go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent
go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger
go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore
go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0
# go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0
## explicit; go 1.23.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace
go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal/tracetransform
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0
# go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
## explicit; go 1.23.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal
@@ -2239,7 +2239,7 @@ go.opentelemetry.io/otel/trace
go.opentelemetry.io/otel/trace/embedded
go.opentelemetry.io/otel/trace/internal/telemetry
go.opentelemetry.io/otel/trace/noop
# go.opentelemetry.io/proto/otlp v1.7.0
# go.opentelemetry.io/proto/otlp v1.7.1
## explicit; go 1.23.0
go.opentelemetry.io/proto/otlp/collector/trace/v1
go.opentelemetry.io/proto/otlp/common/v1
@@ -2434,7 +2434,7 @@ golang.org/x/xerrors/internal
# google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb
## explicit; go 1.23.0
google.golang.org/genproto/protobuf/field_mask
# google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c
# google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5
## explicit; go 1.23.0
google.golang.org/genproto/googleapis/api
google.golang.org/genproto/googleapis/api/annotations