diff --git a/changelog/unreleased/find-groups.md b/changelog/unreleased/find-groups.md new file mode 100644 index 0000000000..88731e0ccf --- /dev/null +++ b/changelog/unreleased/find-groups.md @@ -0,0 +1,6 @@ +Enhancement: Add logic for finding groups to user provider service + +To create shares with user groups, the functionality for searching for these +based on a pattern is needed. This PR adds that. + +https://github.com/cs3org/reva/pull/1239 diff --git a/go.mod b/go.mod index 8a24a2b20e..da583abc72 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/cheggaaa/pb v1.0.29 github.com/coreos/go-oidc v2.2.1+incompatible github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e - github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d + github.com/cs3org/go-cs3apis v0.0.0-20201007120910-416ed6cf8b00 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/eventials/go-tus v0.0.0-20200718001131-45c7ec8f5d59 github.com/go-ldap/ldap/v3 v3.2.4 diff --git a/go.sum b/go.sum index 557bb07879..4111ce49c8 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e h1:tqSPWQeueWTKnJVMJff github.com/cs3org/cato v0.0.0-20200828125504-e418fc54dd5e/go.mod h1:XJEZ3/EQuI3BXTp/6DUzFr850vlxq11I6satRtz0YQ4= github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d h1:YDnGz3eTIYQDXzJd/zefEsl0qbz/P63e8KWjSjYlb5Q= github.com/cs3org/go-cs3apis v0.0.0-20200929101248-821df597ec8d/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= +github.com/cs3org/go-cs3apis v0.0.0-20201007120910-416ed6cf8b00 h1:LVl25JaflluOchVvaHWtoCynm5OaM+VNai0IYkcCSe0= +github.com/cs3org/go-cs3apis v0.0.0-20201007120910-416ed6cf8b00/go.mod h1:UXha4TguuB52H14EMoSsCqDj7k8a/t7g4gVP+bgY5LY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/grpc/services/gateway/userprovider.go b/internal/grpc/services/gateway/userprovider.go index 87c9e354c1..1cac65b75e 100644 --- a/internal/grpc/services/gateway/userprovider.go +++ b/internal/grpc/services/gateway/userprovider.go @@ -69,7 +69,23 @@ func (s *svc) FindUsers(ctx context.Context, req *user.FindUsersRequest) (*user. res, err := c.FindUsers(ctx, req) if err != nil { - return nil, errors.Wrap(err, "gateway: error calling GetUser") + return nil, errors.Wrap(err, "gateway: error calling FindUsers") + } + + return res, nil +} + +func (s *svc) FindGroups(ctx context.Context, req *user.FindGroupsRequest) (*user.FindGroupsResponse, error) { + c, err := pool.GetUserProviderServiceClient(s.c.UserProviderEndpoint) + if err != nil { + return &user.FindGroupsResponse{ + Status: status.NewInternal(ctx, err, "error getting auth client"), + }, nil + } + + res, err := c.FindGroups(ctx, req) + if err != nil { + return nil, errors.Wrap(err, "gateway: error calling FindGroups") } return res, nil @@ -85,7 +101,7 @@ func (s *svc) GetUserGroups(ctx context.Context, req *user.GetUserGroupsRequest) res, err := c.GetUserGroups(ctx, req) if err != nil { - return nil, errors.Wrap(err, "gateway: error calling GetUser") + return nil, errors.Wrap(err, "gateway: error calling GetUserGroups") } return res, nil @@ -101,7 +117,7 @@ func (s *svc) IsInGroup(ctx context.Context, req *user.IsInGroupRequest) (*user. res, err := c.IsInGroup(ctx, req) if err != nil { - return nil, errors.Wrap(err, "gateway: error calling GetUser") + return nil, errors.Wrap(err, "gateway: error calling IsInGroup") } return res, nil diff --git a/internal/grpc/services/userprovider/userprovider.go b/internal/grpc/services/userprovider/userprovider.go index 46f31eb9da..5ca4d5ab57 100644 --- a/internal/grpc/services/userprovider/userprovider.go +++ b/internal/grpc/services/userprovider/userprovider.go @@ -151,6 +151,23 @@ func (s *service) FindUsers(ctx context.Context, req *userpb.FindUsersRequest) ( return res, nil } +func (s *service) FindGroups(ctx context.Context, req *userpb.FindGroupsRequest) (*userpb.FindGroupsResponse, error) { + groups, err := s.usermgr.FindGroups(ctx, req.Filter) + if err != nil { + err = errors.Wrap(err, "userprovidersvc: error finding groups") + res := &userpb.FindGroupsResponse{ + Status: status.NewInternal(ctx, err, "error finding groups"), + } + return res, nil + } + + res := &userpb.FindGroupsResponse{ + Status: status.NewOK(ctx), + Groups: groups, + } + return res, nil +} + func (s *service) GetUserGroups(ctx context.Context, req *userpb.GetUserGroupsRequest) (*userpb.GetUserGroupsResponse, error) { groups, err := s.usermgr.GetUserGroups(ctx, req.UserId) if err != nil { diff --git a/internal/http/services/metrics/metrics.go b/internal/http/services/metrics/metrics.go index 3ac1680afb..c51d472049 100644 --- a/internal/http/services/metrics/metrics.go +++ b/internal/http/services/metrics/metrics.go @@ -49,7 +49,9 @@ func (s *svc) Close() error { // Prefix returns the main endpoint of this service. func (s *svc) Prefix() string { - return "" + // We use a dummy endpoint as the service is not expected to be exposed + // directly to the user, but just start a background process. + return "register_metrics" } // Unprotected returns all endpoints that can be queried without prior authorization. diff --git a/pkg/user/manager/demo/demo.go b/pkg/user/manager/demo/demo.go index e4286192ba..9ef648e6e1 100644 --- a/pkg/user/manager/demo/demo.go +++ b/pkg/user/manager/demo/demo.go @@ -95,6 +95,23 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, return users, nil } +func (m *manager) FindGroups(ctx context.Context, query string) ([]string, error) { + groupSet := make(map[string]bool) + for _, u := range m.catalog { + for _, g := range u.Groups { + if strings.Contains(g, query) { + groupSet[g] = true + } + } + } + + groups := []string{} + for k := range groupSet { + groups = append(groups, k) + } + return groups, nil +} + func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { user, err := m.GetUser(ctx, uid) if err != nil { diff --git a/pkg/user/manager/demo/demo_test.go b/pkg/user/manager/demo/demo_test.go index f94963109c..82966dcc3c 100644 --- a/pkg/user/manager/demo/demo_test.go +++ b/pkg/user/manager/demo/demo_test.go @@ -96,6 +96,15 @@ func TestUserManager(t *testing.T) { t.Fatalf("user not in group: expected=%v got=%v", []*userpb.User{}, resUsers) } + // test FindGroups + resFindGroups, _ := manager.FindGroups(ctx, "violin") + if len(resFindGroups) != 1 { + t.Fatalf("too many groups found: expected=%d got=%+v", 1, resFindGroups) + } + if resFindGroups[0] != "violin-haters" { + t.Fatalf("group differs: expected=%v got=%v", "violin-haters", resFindGroups[0]) + } + // positive test IsInGroup resInGroup, _ := manager.IsInGroup(ctx, uidEinstein, "physics-lovers") if !resInGroup { diff --git a/pkg/user/manager/json/json.go b/pkg/user/manager/json/json.go index 7202082e0a..5a972127aa 100644 --- a/pkg/user/manager/json/json.go +++ b/pkg/user/manager/json/json.go @@ -137,6 +137,23 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, return users, nil } +func (m *manager) FindGroups(ctx context.Context, query string) ([]string, error) { + groupSet := make(map[string]bool) + for _, u := range m.users { + for _, g := range u.Groups { + if strings.Contains(g, query) { + groupSet[g] = true + } + } + } + + groups := []string{} + for k := range groupSet { + groups = append(groups, k) + } + return groups, nil +} + func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { user, err := m.GetUser(ctx, uid) if err != nil { diff --git a/pkg/user/manager/json/json_test.go b/pkg/user/manager/json/json_test.go index 52ced0df2a..0c59592fea 100644 --- a/pkg/user/manager/json/json_test.go +++ b/pkg/user/manager/json/json_test.go @@ -135,6 +135,15 @@ func TestUserManager(t *testing.T) { t.Fatalf("user differ: expected=%v got=%v", "einstein", resUser[0].Username) } + // test FindGroups + resFindGroups, _ := manager.FindGroups(ctx, "violin") + if len(resFindGroups) != 1 { + t.Fatalf("too many groups found: expected=%d got=%+v", 1, resFindGroups) + } + if resFindGroups[0] != "violin-haters" { + t.Fatalf("group differs: expected=%v got=%v", "violin-haters", resFindGroups[0]) + } + // positive test IsInGroup resInGroup, _ := manager.IsInGroup(ctx, uidEinstein, "physics-lovers") if !resInGroup { diff --git a/pkg/user/manager/ldap/ldap.go b/pkg/user/manager/ldap/ldap.go index 420f5b8290..9e41e3d67d 100644 --- a/pkg/user/manager/ldap/ldap.go +++ b/pkg/user/manager/ldap/ldap.go @@ -344,6 +344,10 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, return users, nil } +func (m *manager) FindGroups(ctx context.Context, query string) ([]string, error) { + return nil, errtypes.NotSupported("ldap: FindGroups is not supported") +} + func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", m.c.Hostname, m.c.Port), &tls.Config{InsecureSkipVerify: true}) if err != nil { diff --git a/pkg/user/manager/rest/rest.go b/pkg/user/manager/rest/rest.go index 8b476bf688..5d0e7a3248 100644 --- a/pkg/user/manager/rest/rest.go +++ b/pkg/user/manager/rest/rest.go @@ -420,7 +420,7 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, } } - userSlice := make([]*userpb.User, len(users)) + userSlice := []*userpb.User{} for _, v := range users { userSlice = append(userSlice, v) } @@ -428,6 +428,30 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, return userSlice, nil } +func (m *manager) FindGroups(ctx context.Context, query string) ([]string, error) { + url := fmt.Sprintf("%s/Group?filter=groupIdentifier:contains:%s", m.conf.APIBaseURL, query) + responseData, err := m.sendAPIRequest(ctx, url) + if err != nil { + return nil, err + } + + groupSet := make(map[string]bool) + for _, g := range responseData { + group, ok := g.(map[string]interface{}) + if !ok { + return nil, errors.New("rest: error in type assertion") + } + groupSet[group["groupIdentifier"].(string)] = true + } + + groups := []string{} + for k := range groupSet { + groups = append(groups, k) + } + + return groups, nil +} + func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { groups, err := m.fetchCachedUserGroups(uid) diff --git a/pkg/user/user.go b/pkg/user/user.go index dd22062f82..5c592dff85 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -69,4 +69,5 @@ type Manager interface { GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) IsInGroup(ctx context.Context, uid *userpb.UserId, group string) (bool, error) FindUsers(ctx context.Context, query string) ([]*userpb.User, error) + FindGroups(ctx context.Context, query string) ([]string, error) }