This commit is contained in:
Andre Duffeck
2025-08-07 12:48:52 +02:00
committed by GitHub
parent 9659e97056
commit 05b80b7f63
14 changed files with 101 additions and 30 deletions
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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=
@@ -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"),
+5 -1
View File
@@ -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
+37 -4
View File
@@ -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",
},
}
}
+6 -3
View File
@@ -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
+3 -2
View File
@@ -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
}
+5 -1
View File
@@ -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) {
@@ -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
@@ -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 {
+7 -2
View File
@@ -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
}
+1 -1
View File
@@ -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)
}
+15 -5
View File
@@ -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,
+1 -1
View File
@@ -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