From 9e1e1cc628dd2505b83e4b89e72227ffe6ff1c28 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Wed, 3 Feb 2021 11:40:53 +0100 Subject: [PATCH 1/2] CERNBox REST driver for groupprovider service --- .../unreleased/cbox-groups-rest-driver.md | 3 + pkg/cbox/group/rest/cache.go | 173 +++++++ pkg/cbox/group/rest/rest.go | 468 ++++++++++++++++++ pkg/cbox/loader/loader.go | 1 + pkg/cbox/user/rest/cache.go | 22 +- 5 files changed, 656 insertions(+), 11 deletions(-) create mode 100644 changelog/unreleased/cbox-groups-rest-driver.md create mode 100644 pkg/cbox/group/rest/cache.go create mode 100644 pkg/cbox/group/rest/rest.go diff --git a/changelog/unreleased/cbox-groups-rest-driver.md b/changelog/unreleased/cbox-groups-rest-driver.md new file mode 100644 index 0000000000..ae89eb22b9 --- /dev/null +++ b/changelog/unreleased/cbox-groups-rest-driver.md @@ -0,0 +1,3 @@ +Enhancement: CERNBox REST driver for groupprovider service + +https://github.com/cs3org/reva/pull/1434 diff --git a/pkg/cbox/group/rest/cache.go b/pkg/cbox/group/rest/cache.go new file mode 100644 index 0000000000..80da1d4218 --- /dev/null +++ b/pkg/cbox/group/rest/cache.go @@ -0,0 +1,173 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package rest + +import ( + "encoding/json" + "errors" + "strconv" + "time" + + grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + "github.com/gomodule/redigo/redis" +) + +const ( + groupPrefix = "group:" + groupMembersPrefix = "members:" + groupInternalIDPrefix = "internal:" +) + +func initRedisPool(address, username, password string) *redis.Pool { + return &redis.Pool{ + + MaxIdle: 50, + MaxActive: 1000, + IdleTimeout: 240 * time.Second, + + Dial: func() (redis.Conn, error) { + var c redis.Conn + var err error + switch { + case username != "": + c, err = redis.Dial("tcp", address, + redis.DialUsername(username), + redis.DialPassword(password), + ) + case password != "": + c, err = redis.Dial("tcp", address, + redis.DialPassword(password), + ) + default: + c, err = redis.Dial("tcp", address) + } + + if err != nil { + return nil, err + } + return c, err + }, + + TestOnBorrow: func(c redis.Conn, t time.Time) error { + _, err := c.Do("PING") + return err + }, + } +} + +func (m *manager) setVal(key, val string, expiration int) error { + conn := m.redisPool.Get() + defer conn.Close() + if conn != nil { + if expiration != -1 { + if _, err := conn.Do("SET", key, val, "EX", expiration); err != nil { + return err + } + } else { + if _, err := conn.Do("SET", key, val); err != nil { + return err + } + } + return nil + } + return errors.New("rest: unable to get connection from redis pool") +} + +func (m *manager) getVal(key string) (string, error) { + conn := m.redisPool.Get() + defer conn.Close() + if conn != nil { + val, err := redis.String(conn.Do("GET", key)) + if err != nil { + return "", err + } + return val, nil + } + return "", errors.New("rest: unable to get connection from redis pool") +} + +func (m *manager) fetchCachedInternalID(gid *grouppb.GroupId) (string, error) { + return m.getVal(groupPrefix + groupInternalIDPrefix + gid.OpaqueId) +} + +func (m *manager) cacheInternalID(gid *grouppb.GroupId, internalID string) error { + return m.setVal(groupPrefix+groupInternalIDPrefix+gid.OpaqueId, internalID, -1) +} + +func (m *manager) fetchCachedGroupDetails(gid *grouppb.GroupId) (*grouppb.Group, error) { + group, err := m.getVal(groupPrefix + gid.OpaqueId) + if err != nil { + return nil, err + } + + g := grouppb.Group{} + if err = json.Unmarshal([]byte(group), &g); err != nil { + return nil, err + } + return &g, nil +} + +func (m *manager) cacheGroupDetails(g *grouppb.Group) error { + encodedGroup, err := json.Marshal(&g) + if err != nil { + return err + } + if err = m.setVal(groupPrefix+g.Id.OpaqueId, string(encodedGroup), -1); err != nil { + return err + } + + if err = m.setVal(groupPrefix+"gid_number:"+strconv.FormatInt(g.GidNumber, 10), g.Id.OpaqueId, -1); err != nil { + return err + } + if err = m.setVal(groupPrefix+"mail:"+g.Mail, g.Id.OpaqueId, -1); err != nil { + return err + } + if err = m.setVal(groupPrefix+"group_name:"+g.GroupName, g.Id.OpaqueId, -1); err != nil { + return err + } + return nil +} + +func (m *manager) fetchCachedParam(field, claim string) (string, error) { + return m.getVal(groupPrefix + field + ":" + claim) +} + +func (m *manager) fetchCachedGroupMembers(gid *grouppb.GroupId) ([]*userpb.UserId, error) { + members, err := m.getVal(groupPrefix + groupMembersPrefix + gid.OpaqueId) + if err != nil { + return nil, err + } + u := []*userpb.UserId{} + if err = json.Unmarshal([]byte(members), &u); err != nil { + return nil, err + } + return u, nil +} + +func (m *manager) cacheGroupMembers(gid *grouppb.GroupId, members []*userpb.UserId) error { + u, err := json.Marshal(&members) + if err != nil { + return err + } + if err = m.setVal(groupPrefix+groupMembersPrefix+gid.OpaqueId, string(u), m.conf.GroupMembersCacheExpiration*60); err != nil { + return err + } + return nil +} diff --git a/pkg/cbox/group/rest/rest.go b/pkg/cbox/group/rest/rest.go new file mode 100644 index 0000000000..64f30fe251 --- /dev/null +++ b/pkg/cbox/group/rest/rest.go @@ -0,0 +1,468 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package rest + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "strings" + "sync" + "time" + + grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/group" + "github.com/cs3org/reva/pkg/group/manager/registry" + "github.com/cs3org/reva/pkg/rhttp" + "github.com/gomodule/redigo/redis" + "github.com/mitchellh/mapstructure" +) + +func init() { + registry.Register("rest", New) +} + +var ( + emailRegex = regexp.MustCompile(`^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$`) + groupNameRegex = regexp.MustCompile(`^[ a-zA-Z0-9._-]+$`) +) + +type manager struct { + conf *config + redisPool *redis.Pool + oidcToken OIDCToken + client *http.Client +} + +// OIDCToken stores the OIDC token used to authenticate requests to the REST API service +type OIDCToken struct { + sync.Mutex // concurrent access to apiToken and tokenExpirationTime + apiToken string + tokenExpirationTime time.Time +} + +type config struct { + // The address at which the redis server is running + RedisAddress string `mapstructure:"redis_address" docs:"localhost:6379"` + // The username for connecting to the redis server + RedisUsername string `mapstructure:"redis_username" docs:""` + // The password for connecting to the redis server + RedisPassword string `mapstructure:"redis_password" docs:""` + // The time in minutes for which the members of a group would be cached + GroupMembersCacheExpiration int `mapstructure:"user_groups_cache_expiration" docs:"5"` + // The OIDC Provider + IDProvider string `mapstructure:"id_provider" docs:"http://cernbox.cern.ch"` + // Base API Endpoint + APIBaseURL string `mapstructure:"api_base_url" docs:"https://authorization-service-api-dev.web.cern.ch/api/v1.0"` + // Client ID needed to authenticate + ClientID string `mapstructure:"client_id" docs:"-"` + // Client Secret + ClientSecret string `mapstructure:"client_secret" docs:"-"` + + // Endpoint to generate token to access the API + OIDCTokenEndpoint string `mapstructure:"oidc_token_endpoint" docs:"https://keycloak-dev.cern.ch/auth/realms/cern/api-access/token"` + // The target application for which token needs to be generated + TargetAPI string `mapstructure:"target_api" docs:"authorization-service-api"` +} + +func (c *config) init() { + if c.GroupMembersCacheExpiration == 0 { + c.GroupMembersCacheExpiration = 5 + } + if c.RedisAddress == "" { + c.RedisAddress = ":6379" + } + if c.APIBaseURL == "" { + c.APIBaseURL = "https://authorization-service-api-dev.web.cern.ch/api/v1.0" + } + if c.TargetAPI == "" { + c.TargetAPI = "authorization-service-api" + } + if c.OIDCTokenEndpoint == "" { + c.OIDCTokenEndpoint = "https://keycloak-dev.cern.ch/auth/realms/cern/api-access/token" + } + if c.IDProvider == "" { + c.IDProvider = "http://cernbox.cern.ch" + } +} + +func parseConfig(m map[string]interface{}) (*config, error) { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + return nil, err + } + return c, nil +} + +// New returns a user manager implementation that makes calls to the GRAPPA API. +func New(m map[string]interface{}) (group.Manager, error) { + c, err := parseConfig(m) + if err != nil { + return nil, err + } + c.init() + + redisPool := initRedisPool(c.RedisAddress, c.RedisUsername, c.RedisPassword) + return &manager{ + conf: c, + redisPool: redisPool, + client: rhttp.GetHTTPClient( + rhttp.Timeout(10*time.Second), + rhttp.Insecure(true), + ), + }, nil +} + +func (m *manager) renewAPIToken(ctx context.Context) error { + // Received tokens have an expiration time of 20 minutes. + // Take a couple of seconds as buffer time for the API call to complete + if m.oidcToken.tokenExpirationTime.Before(time.Now().Add(time.Second * time.Duration(2))) { + token, expiration, err := m.getAPIToken(ctx) + if err != nil { + return err + } + + m.oidcToken.Lock() + defer m.oidcToken.Unlock() + + m.oidcToken.apiToken = token + m.oidcToken.tokenExpirationTime = expiration + } + return nil +} + +func (m *manager) getAPIToken(ctx context.Context) (string, time.Time, error) { + + params := url.Values{ + "grant_type": {"client_credentials"}, + "audience": {m.conf.TargetAPI}, + } + + httpReq, err := http.NewRequest("POST", m.conf.OIDCTokenEndpoint, strings.NewReader(params.Encode())) + if err != nil { + return "", time.Time{}, err + } + httpReq.SetBasicAuth(m.conf.ClientID, m.conf.ClientSecret) + httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + + httpRes, err := m.client.Do(httpReq) + if err != nil { + return "", time.Time{}, err + } + defer httpRes.Body.Close() + + body, err := ioutil.ReadAll(httpRes.Body) + if err != nil { + return "", time.Time{}, err + } + + var result map[string]interface{} + err = json.Unmarshal(body, &result) + if err != nil { + return "", time.Time{}, err + } + + expirationSecs := result["expires_in"].(float64) + expirationTime := time.Now().Add(time.Second * time.Duration(expirationSecs)) + return result["access_token"].(string), expirationTime, nil +} + +func (m *manager) sendAPIRequest(ctx context.Context, url string) ([]interface{}, error) { + err := m.renewAPIToken(ctx) + if err != nil { + return nil, err + } + + httpReq, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + // We don't need to take the lock when reading apiToken, because if we reach here, + // the token is valid at least for a couple of seconds. Even if another request modifies + // the token and expiration time while this request is in progress, the current token will still be valid. + httpReq.Header.Set("Authorization", "Bearer "+m.oidcToken.apiToken) + + httpRes, err := m.client.Do(httpReq) + if err != nil { + return nil, err + } + defer httpRes.Body.Close() + + body, err := ioutil.ReadAll(httpRes.Body) + if err != nil { + return nil, err + } + + var result map[string]interface{} + err = json.Unmarshal(body, &result) + if err != nil { + return nil, err + } + + responseData, ok := result["data"].([]interface{}) + if !ok { + return nil, errors.New("rest: error in type assertion") + } + + return responseData, nil +} + +func (m *manager) getGroupByParam(ctx context.Context, param, val string) (map[string]interface{}, error) { + url := fmt.Sprintf("%s/Group?filter=%s:%s&field=groupIdentifier&field=displayName&field=gid", + m.conf.APIBaseURL, param, val) + responseData, err := m.sendAPIRequest(ctx, url) + if err != nil { + return nil, err + } + if len(responseData) == 0 { + return nil, errors.New("rest: no user found") + } + + userData, ok := responseData[0].(map[string]interface{}) + if !ok { + return nil, errors.New("rest: error in type assertion") + } + return userData, nil +} + +func (m *manager) getInternalGroupID(ctx context.Context, gid *grouppb.GroupId) (string, error) { + + internalID, err := m.fetchCachedInternalID(gid) + if err != nil { + groupData, err := m.getGroupByParam(ctx, "groupIdentifier", gid.OpaqueId) + if err != nil { + return "", err + } + id, ok := groupData["id"].(string) + if !ok { + return "", errors.New("rest: error in type assertion") + } + + if err = m.cacheInternalID(gid, id); err != nil { + log := appctx.GetLogger(ctx) + log.Error().Err(err).Msg("rest: error caching group details") + } + return id, nil + } + return internalID, nil +} + +func (m *manager) parseAndCacheGroup(ctx context.Context, groupData map[string]interface{}) *grouppb.Group { + groupID := &grouppb.GroupId{ + OpaqueId: groupData["groupIdentifier"].(string), + Idp: m.conf.IDProvider, + } + gid, ok := groupData["gid"].(int64) + if !ok { + gid = 0 + } + g := &grouppb.Group{ + Id: groupID, + GroupName: groupData["groupIdentifier"].(string), + Mail: groupData["groupIdentifier"].(string) + "@cern.ch", + DisplayName: groupData["displayName"].(string), + GidNumber: gid, + } + + if err := m.cacheGroupDetails(g); err != nil { + log := appctx.GetLogger(ctx) + log.Error().Err(err).Msg("rest: error caching group details") + } + if err := m.cacheInternalID(groupID, groupData["id"].(string)); err != nil { + log := appctx.GetLogger(ctx) + log.Error().Err(err).Msg("rest: error caching group details") + } + return g + +} + +func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId) (*grouppb.Group, error) { + + g, err := m.fetchCachedGroupDetails(gid) + if err != nil { + groupData, err := m.getGroupByParam(ctx, "groupIdentifier", gid.OpaqueId) + if err != nil { + return nil, err + } + g = m.parseAndCacheGroup(ctx, groupData) + } + + groupMembers, err := m.GetMembers(ctx, gid) + if err != nil { + return nil, err + } + g.Members = groupMembers + + return g, nil +} + +func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string) (*grouppb.Group, error) { + opaqueID, err := m.fetchCachedParam(claim, value) + if err == nil { + return m.GetGroup(ctx, &grouppb.GroupId{OpaqueId: opaqueID}) + } + + switch claim { + case "mail": + claim = "mail" + case "gid_number": + claim = "gid_number" + case "group_name": + claim = "group_name" + default: + return nil, errors.New("rest: invalid field") + } + + groupData, err := m.getGroupByParam(ctx, claim, value) + if err != nil { + return nil, err + } + g := m.parseAndCacheGroup(ctx, groupData) + + groupMembers, err := m.GetMembers(ctx, g.Id) + if err != nil { + return nil, err + } + g.Members = groupMembers + + return g, nil + +} + +func (m *manager) findGroupsByFilter(ctx context.Context, url string, groups map[string]*grouppb.Group) error { + + groupData, err := m.sendAPIRequest(ctx, url) + if err != nil { + return err + } + + for _, grp := range groupData { + grpInfo, ok := grp.(map[string]interface{}) + if !ok { + return errors.New("rest: error in type assertion") + } + + groupID := &grouppb.GroupId{ + OpaqueId: grpInfo["groupIdentifier"].(string), + Idp: m.conf.IDProvider, + } + gid, ok := grpInfo["gid"].(int64) + if !ok { + gid = 0 + } + groups[groupID.OpaqueId] = &grouppb.Group{ + Id: groupID, + GroupName: grpInfo["groupidentifier"].(string), + Mail: grpInfo["groupIdentifier"].(string) + "@cern.ch", + DisplayName: grpInfo["displayName"].(string), + GidNumber: gid, + } + } + + return nil +} + +func (m *manager) FindGroups(ctx context.Context, query string) ([]*grouppb.Group, error) { + + var filters []string + switch { + case groupNameRegex.MatchString(query): + filters = []string{"groupidentifier", "displayName"} + case emailRegex.MatchString(query): + filters = []string{"groupidentifier"} + query = strings.TrimSuffix(query, "@cern.ch") + default: + return nil, errors.New("rest: illegal characters present in query") + } + + groups := make(map[string]*grouppb.Group) + + for _, f := range filters { + url := fmt.Sprintf("%s/Groups/?filter=%s:contains:%s&field=groupIdentifier&field=displayName&field=gid", + m.conf.APIBaseURL, f, query) + err := m.findGroupsByFilter(ctx, url, groups) + if err != nil { + return nil, err + } + } + + groupSlice := []*grouppb.Group{} + for _, v := range groups { + groupSlice = append(groupSlice, v) + } + + return groupSlice, nil +} + +func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*userpb.UserId, error) { + + users, err := m.fetchCachedGroupMembers(gid) + if err == nil { + return users, nil + } + + internalID, err := m.getInternalGroupID(ctx, gid) + if err != nil { + return nil, err + } + url := fmt.Sprintf("%s/Group/%s/membergroups/precomputed", m.conf.APIBaseURL, internalID) + userData, err := m.sendAPIRequest(ctx, url) + if err != nil { + return nil, err + } + + users = []*userpb.UserId{} + + for _, u := range userData { + userInfo, ok := u.(map[string]interface{}) + if !ok { + return nil, errors.New("rest: error in type assertion") + } + users = append(users, &userpb.UserId{OpaqueId: userInfo["upn"].(string), Idp: m.conf.IDProvider}) + } + + if err = m.cacheGroupMembers(gid, users); err != nil { + log := appctx.GetLogger(ctx) + log.Error().Err(err).Msg("rest: error caching group members") + } + + return users, nil +} + +func (m *manager) HasMember(ctx context.Context, gid *grouppb.GroupId, uid *userpb.UserId) (bool, error) { + groupMemers, err := m.GetMembers(ctx, gid) + if err != nil { + return false, err + } + + for _, u := range groupMemers { + if uid == u { + return true, nil + } + } + return false, nil +} diff --git a/pkg/cbox/loader/loader.go b/pkg/cbox/loader/loader.go index 53e11558ae..8cfab6636e 100644 --- a/pkg/cbox/loader/loader.go +++ b/pkg/cbox/loader/loader.go @@ -20,6 +20,7 @@ package loader import ( // Load cbox specific drivers. + _ "github.com/cs3org/reva/pkg/cbox/group/rest" _ "github.com/cs3org/reva/pkg/cbox/share/sql" _ "github.com/cs3org/reva/pkg/cbox/user/rest" ) diff --git a/pkg/cbox/user/rest/cache.go b/pkg/cbox/user/rest/cache.go index 5f9f2611d6..b89c2a8923 100644 --- a/pkg/cbox/user/rest/cache.go +++ b/pkg/cbox/user/rest/cache.go @@ -28,7 +28,7 @@ import ( ) const ( - userDetailsPrefix = "user:" + userPrefix = "user:" userGroupsPrefix = "groups:" userInternalIDPrefix = "internal:" ) @@ -102,15 +102,15 @@ func (m *manager) getVal(key string) (string, error) { } func (m *manager) fetchCachedInternalID(uid *userpb.UserId) (string, error) { - return m.getVal(userInternalIDPrefix + uid.OpaqueId) + return m.getVal(userPrefix + userInternalIDPrefix + uid.OpaqueId) } func (m *manager) cacheInternalID(uid *userpb.UserId, internalID string) error { - return m.setVal(userInternalIDPrefix+uid.OpaqueId, internalID, -1) + return m.setVal(userPrefix+userInternalIDPrefix+uid.OpaqueId, internalID, -1) } func (m *manager) fetchCachedUserDetails(uid *userpb.UserId) (*userpb.User, error) { - user, err := m.getVal(userDetailsPrefix + uid.OpaqueId) + user, err := m.getVal(userPrefix + uid.OpaqueId) if err != nil { return nil, err } @@ -127,7 +127,7 @@ func (m *manager) cacheUserDetails(u *userpb.User) error { if err != nil { return err } - if err = m.setVal(userDetailsPrefix+u.Id.OpaqueId, string(encodedUser), -1); err != nil { + if err = m.setVal(userPrefix+u.Id.OpaqueId, string(encodedUser), -1); err != nil { return err } @@ -136,24 +136,24 @@ func (m *manager) cacheUserDetails(u *userpb.User) error { return err } - if err = m.setVal("uid:"+uid, u.Id.OpaqueId, -1); err != nil { + if err = m.setVal(userPrefix+"uid:"+uid, u.Id.OpaqueId, -1); err != nil { return err } - if err = m.setVal("mail:"+u.Mail, u.Id.OpaqueId, -1); err != nil { + if err = m.setVal(userPrefix+"mail:"+u.Mail, u.Id.OpaqueId, -1); err != nil { return err } - if err = m.setVal("username:"+u.Username, u.Id.OpaqueId, -1); err != nil { + if err = m.setVal(userPrefix+"username:"+u.Username, u.Id.OpaqueId, -1); err != nil { return err } return nil } func (m *manager) fetchCachedParam(field, claim string) (string, error) { - return m.getVal(field + ":" + claim) + return m.getVal(userPrefix + field + ":" + claim) } func (m *manager) fetchCachedUserGroups(uid *userpb.UserId) ([]string, error) { - groups, err := m.getVal(userGroupsPrefix + uid.OpaqueId) + groups, err := m.getVal(userPrefix + userGroupsPrefix + uid.OpaqueId) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (m *manager) cacheUserGroups(uid *userpb.UserId, groups []string) error { if err != nil { return err } - if err = m.setVal(userGroupsPrefix+uid.OpaqueId, string(g), m.conf.UserGroupsCacheExpiration*60); err != nil { + if err = m.setVal(userPrefix+userGroupsPrefix+uid.OpaqueId, string(g), m.conf.UserGroupsCacheExpiration*60); err != nil { return err } return nil From c2bd91f3a547da508fc349771ffbbb627ee97469 Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Thu, 4 Feb 2021 14:09:51 +0100 Subject: [PATCH 2/2] Fixes --- pkg/cbox/group/rest/rest.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/cbox/group/rest/rest.go b/pkg/cbox/group/rest/rest.go index 64f30fe251..9abf4217e9 100644 --- a/pkg/cbox/group/rest/rest.go +++ b/pkg/cbox/group/rest/rest.go @@ -328,11 +328,12 @@ func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string) (*gr switch claim { case "mail": - claim = "mail" + claim = "groupIdentifier" + value = strings.TrimSuffix(value, "@cern.ch") case "gid_number": - claim = "gid_number" + claim = "gid" case "group_name": - claim = "group_name" + claim = "groupIdentifier" default: return nil, errors.New("rest: invalid field") } @@ -376,7 +377,7 @@ func (m *manager) findGroupsByFilter(ctx context.Context, url string, groups map } groups[groupID.OpaqueId] = &grouppb.Group{ Id: groupID, - GroupName: grpInfo["groupidentifier"].(string), + GroupName: grpInfo["groupIdentifier"].(string), Mail: grpInfo["groupIdentifier"].(string) + "@cern.ch", DisplayName: grpInfo["displayName"].(string), GidNumber: gid, @@ -402,7 +403,7 @@ func (m *manager) FindGroups(ctx context.Context, query string) ([]*grouppb.Grou groups := make(map[string]*grouppb.Group) for _, f := range filters { - url := fmt.Sprintf("%s/Groups/?filter=%s:contains:%s&field=groupIdentifier&field=displayName&field=gid", + url := fmt.Sprintf("%s/Group/?filter=%s:contains:%s&field=groupIdentifier&field=displayName&field=gid", m.conf.APIBaseURL, f, query) err := m.findGroupsByFilter(ctx, url, groups) if err != nil { @@ -429,7 +430,7 @@ func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*user if err != nil { return nil, err } - url := fmt.Sprintf("%s/Group/%s/membergroups/precomputed", m.conf.APIBaseURL, internalID) + url := fmt.Sprintf("%s/Group/%s/memberidentities/precomputed", m.conf.APIBaseURL, internalID) userData, err := m.sendAPIRequest(ctx, url) if err != nil { return nil, err @@ -460,7 +461,7 @@ func (m *manager) HasMember(ctx context.Context, gid *grouppb.GroupId, uid *user } for _, u := range groupMemers { - if uid == u { + if uid.OpaqueId == u.OpaqueId { return true, nil } }