diff --git a/go.mod b/go.mod index a42467386..5020e8b18 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/onsi/gomega v1.37.0 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.35.1-0.20250805150512-1bcca91111ef + github.com/opencloud-eu/reva/v2 v2.35.1-0.20250806151828-b3ba930998b3 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.12 diff --git a/go.sum b/go.sum index a78aa8aae..e475d498b 100644 --- a/go.sum +++ b/go.sum @@ -868,8 +868,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.35.1-0.20250805150512-1bcca91111ef h1:hGdTxp1Q4smixC5t8kCoD5ByDArrlMYOWwM2IIfUpjw= -github.com/opencloud-eu/reva/v2 v2.35.1-0.20250805150512-1bcca91111ef/go.mod h1:/FyYaUWxtllu8TOcIIx53BjChc+hSpcQicBI/OTICjw= +github.com/opencloud-eu/reva/v2 v2.35.1-0.20250806151828-b3ba930998b3 h1:vFkByH3taFxFGav4lifAJjMcuYE1TopUtNIMYz4jqYw= +github.com/opencloud-eu/reva/v2 v2.35.1-0.20250806151828-b3ba930998b3/go.mod h1:/FyYaUWxtllu8TOcIIx53BjChc+hSpcQicBI/OTICjw= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go index 27ac671f2..921ec2145 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go @@ -24,18 +24,20 @@ import ( "path/filepath" "sort" - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "google.golang.org/grpc" + + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/opencloud-eu/reva/v2/pkg/appctx" + revactx "github.com/opencloud-eu/reva/v2/pkg/ctx" "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/plugin" "github.com/opencloud-eu/reva/v2/pkg/rgrpc" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/status" "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" - "github.com/pkg/errors" - "github.com/rs/zerolog" - "google.golang.org/grpc" ) func init() { @@ -172,7 +174,9 @@ func (s *service) GetUserByClaim(ctx context.Context, req *userpb.GetUserByClaim } func (s *service) FindUsers(ctx context.Context, req *userpb.FindUsersRequest) (*userpb.FindUsersResponse, error) { - users, err := s.usermgr.FindUsers(ctx, req.Filter, req.SkipFetchingUserGroups) + currentUser := revactx.ContextMustGetUser(ctx) + + users, err := s.usermgr.FindUsers(ctx, req.Filter, currentUser.Id.GetTenantId(), req.SkipFetchingUserGroups) if err != nil { res := &userpb.FindUsersResponse{ Status: status.NewInternal(ctx, "error finding users"), diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go index bb9ae677a..86b300010 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/cbox/user/rest/rest.go @@ -32,6 +32,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/opencloud-eu/reva/v2/pkg/appctx" utils "github.com/opencloud-eu/reva/v2/pkg/cbox/utils" + "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" "github.com/pkg/errors" @@ -256,7 +257,10 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF return u, nil } -func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { + if tenantID != "" { + return nil, errtypes.NotSupported("tenant filter not supported in rest user manager") + } // Look at namespaces filters. If the query starts with: // "a" => look into primary/secondary/service accounts diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go index 31921247d..a53d892fe 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go @@ -98,14 +98,20 @@ func extractClaim(u *userpb.User, claim string) (string, error) { } // TODO(jfd) compare sub? -func userContains(u *userpb.User, query string) bool { - return strings.Contains(u.Username, query) || strings.Contains(u.DisplayName, query) || strings.Contains(u.Mail, query) || strings.Contains(u.Id.OpaqueId, query) +func userContains(u *userpb.User, query, tenantID string) bool { + if tenantID != "" && u.Id.TenantId != tenantID { + return false + } + return strings.Contains(u.Username, query) || + strings.Contains(u.DisplayName, query) || + strings.Contains(u.Mail, query) || + strings.Contains(u.Id.OpaqueId, query) } -func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { users := []*userpb.User{} for _, u := range m.catalog { - if userContains(u, query) { + if userContains(u, query, tenantID) { user := proto.Clone(u).(*userpb.User) if skipFetchingGroups { user.Groups = nil @@ -131,6 +137,7 @@ func getUsers() map[string]*userpb.User { Idp: "http://localhost:9998", OpaqueId: "4c510ada-c86b-4815-8820-42cdf82c3d51", Type: userpb.UserType_USER_TYPE_PRIMARY, + TenantId: "c239389d-c249-499d-ae80-07558429769a", }, Username: "einstein", Groups: []string{"sailing-lovers", "violin-haters", "physics-lovers"}, @@ -144,6 +151,7 @@ func getUsers() map[string]*userpb.User { Idp: "http://localhost:9998", OpaqueId: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", Type: userpb.UserType_USER_TYPE_PRIMARY, + TenantId: "c239389d-c249-499d-ae80-07558429769a", }, Username: "marie", Groups: []string{"radium-lovers", "polonium-lovers", "physics-lovers"}, @@ -157,11 +165,36 @@ func getUsers() map[string]*userpb.User { Idp: "http://localhost:9998", OpaqueId: "932b4540-8d16-481e-8ef4-588e4b6b151c", Type: userpb.UserType_USER_TYPE_PRIMARY, + TenantId: "c239389d-c249-499d-ae80-07558429769a", }, Username: "richard", Groups: []string{"quantum-lovers", "philosophy-haters", "physics-lovers"}, Mail: "richard@example.org", DisplayName: "Richard Feynman", }, + "bf2ee2f2-67bc-418f-ac4e-2f6427f9fb2f": { + Id: &userpb.UserId{ + Idp: "http://localhost:9998", + OpaqueId: "bf2ee2f2-67bc-418f-ac4e-2f6427f9fb2f", + Type: userpb.UserType_USER_TYPE_PRIMARY, + TenantId: "d375ba5e-1140-472a-b199-ca7d671a9fe1", + }, + Username: "jen", + Groups: []string{}, + Mail: "jen@example.org", + DisplayName: "Jen Barber", + }, + "79a91ae3-13da-4bab-9f91-63c60295c7cf": { + Id: &userpb.UserId{ + Idp: "http://localhost:9998", + OpaqueId: "79a91ae3-13da-4bab-9f91-63c60295c7cf", + Type: userpb.UserType_USER_TYPE_PRIMARY, + TenantId: "d375ba5e-1140-472a-b199-ca7d671a9fe1", + }, + Username: "ralph", + Groups: []string{}, + Mail: "ralphi@example.org", + DisplayName: "Ralph Wiggum", + }, } } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go index eb58769f1..2b467ec38 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go @@ -138,16 +138,19 @@ func extractClaim(u *userpb.User, claim string) (string, error) { } // TODO(jfd) search Opaque? compare sub? -func userContains(u *userpb.User, query string) bool { +func userContains(u *userpb.User, query, tenantID string) bool { + if tenantID != "" && u.Id.TenantId != tenantID { + return false + } query = strings.ToLower(query) return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) || strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(u.Id.OpaqueId), query) } -func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { users := []*userpb.User{} for _, u := range m.users { - if userContains(u, query) { + if userContains(u, query, tenantID) { user := proto.Clone(u).(*userpb.User) if skipFetchingGroups { user.Groups = nil diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go index 868833d70..1cf69509d 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go @@ -168,9 +168,9 @@ 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 string, skipFetchingGroups bool) ([]*userpb.User, error) { +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) + entries, err := m.c.LDAPIdentity.GetLDAPUsers(log, m.ldapClient, query, tenantID) if err != nil { return nil, err } @@ -261,6 +261,7 @@ func (m *manager) ldapEntryToUserID(entry *ldap.Entry) (*userpb.UserId, error) { return &userpb.UserId{ Idp: m.c.Idp, OpaqueId: uid, + TenantId: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.TenantID), Type: m.c.LDAPIdentity.GetUserType(entry), }, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go index 0d1a2d999..fcdb5a70c 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go @@ -150,7 +150,11 @@ func userContains(u *User, query string) bool { return strings.Contains(u.Username, query) || strings.Contains(u.DisplayName, query) || strings.Contains(u.Mail, query) || strings.Contains(u.ID.OpaqueId, query) } -func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { + if tenantID != "" { + return nil, errtypes.NotSupported("tenant filter not supported in memory user manager") + } + users := []*userpb.User{} for _, u := range m.catalog { if userContains(u, query) { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go index 83f65e67e..5daa94c3f 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go @@ -216,7 +216,11 @@ func (um *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]str } // FindUsers method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35 -func (um *Manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (um *Manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { + if tenantID != "" { + return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager") + } + user, err := getUser(ctx) if err != nil { return nil, err diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go index cf84173a6..f1499725c 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go @@ -121,7 +121,10 @@ func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipF return m.convertToCS3User(ctx, a, skipFetchingGroups) } -func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { + if tenantID != "" { + return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager") + } accounts, err := m.db.FindAccounts(ctx, query) if err == sql.ErrNoRows { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/rpc_user.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/rpc_user.go index b3248a883..aa813c696 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/rpc_user.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/rpc_user.go @@ -26,6 +26,7 @@ import ( userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" hcplugin "github.com/hashicorp/go-plugin" "github.com/opencloud-eu/reva/v2/pkg/appctx" + "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/plugin" ) @@ -162,7 +163,11 @@ type FindUsersReply struct { } // FindUsers RPCClient FindUsers method -func (m *RPCClient) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { +func (m *RPCClient) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) { + if tenantID != "" { + return nil, errtypes.NotSupported("tenant filter not supported in rpc user manager") + } + ctxVal := appctx.GetKeyValuesFromCtx(ctx) args := FindUsersArg{Ctx: ctxVal, Query: query, SkipFetchingGroups: skipFetchingGroups} resp := FindUsersReply{} @@ -209,6 +214,6 @@ func (m *RPCServer) GetUserGroups(args GetUserGroupsArg, resp *GetUserGroupsRepl // FindUsers RPCServer FindUsers method func (m *RPCServer) FindUsers(args FindUsersArg, resp *FindUsersReply) error { ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.User, resp.Err = m.Impl.FindUsers(ctx, args.Query, args.SkipFetchingGroups) + resp.User, resp.Err = m.Impl.FindUsers(ctx, args.Query, "", args.SkipFetchingGroups) return nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/user.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/user.go index 32a028f4d..4814251c4 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/user.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/user.go @@ -37,5 +37,5 @@ type Manager interface { // 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. - FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) + FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go index ee2efe4a3..10dc8c4c0 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/identity.go @@ -94,6 +94,8 @@ type userSchema struct { UIDNumber string `mapstructure:"uidNumber"` // GIDNumber is a numeric id that maps to a filesystem gid, eg. 654321 GIDNumber string `mapstructure:"gidNumber"` + // TenantID is the tenant id of the user, if applicable. + TenantID string `mapstructure:"tenantId"` } // Default userConfig (somewhat inspired by Active Directory) @@ -205,6 +207,7 @@ func (i *Identity) GetLDAPUserByFilter(log *zerolog.Logger, lc ldap.Client, filt []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, @@ -269,8 +272,8 @@ 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 string) ([]*ldap.Entry, error) { - filter := i.getUserFindFilter(query) +func (i *Identity) GetLDAPUsers(log *zerolog.Logger, lc ldap.Client, query, tenantID string) ([]*ldap.Entry, error) { + filter := i.getUserFindFilter(query, tenantID) searchRequest := ldap.NewSearchRequest( i.User.BaseDN, i.User.scopeVal, ldap.NeverDerefAliases, 0, 0, false, @@ -523,6 +526,8 @@ func (i *Identity) getUserAttributeFilter(attribute, value string) (string, erro attribute = i.User.Schema.Username case "userid": attribute = i.User.Schema.ID + case "tenantid": + attribute = i.User.Schema.TenantID default: return "", errors.New("ldap: invalid field " + attribute) } @@ -554,7 +559,7 @@ func (i *Identity) disabledFilter() string { // getUserFindFilter construct a LDAP filter to perform a prefix-substring // search for users. -func (i *Identity) getUserFindFilter(query string) string { +func (i *Identity) getUserFindFilter(query, tenantID string) string { searchAttrs := []string{ i.User.Schema.Mail, i.User.Schema.DisplayName, @@ -573,9 +578,14 @@ func (i *Identity) getUserFindFilter(query string) string { 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)) + filter = fmt.Sprintf("(|%s(%s=%s))", filter, i.User.Schema.ID, ldap.EscapeFilter(query)) - return fmt.Sprintf("(&%s(objectclass=%s)(|%s))", + 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)", i.User.Filter, i.User.Objectclass, filter, diff --git a/vendor/modules.txt b/vendor/modules.txt index 8e7c85173..6558e6e44 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1213,7 +1213,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.35.1-0.20250805150512-1bcca91111ef +# github.com/opencloud-eu/reva/v2 v2.35.1-0.20250806151828-b3ba930998b3 ## explicit; go 1.24.1 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime