mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-25 06:58:59 -06:00
Implement reading Group Members on Graph API
This implement the graph/v1.0/groups/{groupid}/members endpoint.
Starting with the LDAP backend.
This commit is contained in:
@@ -25,6 +25,7 @@ type Backend interface {
|
||||
CreateGroup(ctx context.Context, group libregraph.Group) (*libregraph.Group, error)
|
||||
GetGroup(ctx context.Context, nameOrID string) (*libregraph.Group, error)
|
||||
GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error)
|
||||
GetGroupMembers(ctx context.Context, id string) ([]*libregraph.User, error)
|
||||
}
|
||||
|
||||
func CreateUserModelFromCS3(u *cs3.User) *libregraph.User {
|
||||
|
||||
@@ -174,6 +174,11 @@ func (i *CS3) GetGroup(ctx context.Context, groupID string) (*libregraph.Group,
|
||||
return createGroupModelFromCS3(res.Group), nil
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) GetGroupMembers(ctx context.Context, groupID string) ([]*libregraph.User, error) {
|
||||
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
}
|
||||
|
||||
func createGroupModelFromCS3(g *cs3group.Group) *libregraph.Group {
|
||||
if g.Id == nil {
|
||||
g.Id = &cs3group.GroupId{}
|
||||
|
||||
@@ -364,16 +364,30 @@ func (i *LDAP) GetUsers(ctx context.Context, queryParam url.Values) ([]*libregra
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (i *LDAP) GetGroup(ctx context.Context, groupID string) (*libregraph.Group, error) {
|
||||
func (i *LDAP) GetGroup(ctx context.Context, nameOrID string) (*libregraph.Group, error) {
|
||||
i.logger.Debug().Str("backend", "ldap").Msg("GetGroup")
|
||||
groupID = ldap.EscapeFilter(groupID)
|
||||
e, err := i.getLDAPGroupByNameOrID(nameOrID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i.createGroupModelFromLDAP(e), nil
|
||||
}
|
||||
|
||||
func (i *LDAP) getLDAPGroupByNameOrID(nameOrID string, requestMembers bool) (*ldap.Entry, error) {
|
||||
nameOrID = ldap.EscapeFilter(nameOrID)
|
||||
attrs := []string{
|
||||
i.groupAttributeMap.name,
|
||||
i.groupAttributeMap.id,
|
||||
}
|
||||
|
||||
if requestMembers {
|
||||
attrs = append(attrs, i.groupAttributeMap.member)
|
||||
}
|
||||
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
i.groupBaseDN, i.groupScope, ldap.NeverDerefAliases, 1, 0, false,
|
||||
fmt.Sprintf("(&%s(|(%s=%s)(%s=%s)))", i.groupFilter, i.groupAttributeMap.name, groupID, i.groupAttributeMap.id, groupID),
|
||||
[]string{
|
||||
i.groupAttributeMap.name,
|
||||
i.groupAttributeMap.id,
|
||||
},
|
||||
fmt.Sprintf("(&%s(|(%s=%s)(%s=%s)))", i.groupFilter, i.groupAttributeMap.name, nameOrID, i.groupAttributeMap.id, nameOrID),
|
||||
attrs,
|
||||
nil,
|
||||
)
|
||||
i.logger.Debug().Str("backend", "ldap").Msgf("Search %s", i.groupBaseDN)
|
||||
@@ -383,7 +397,7 @@ func (i *LDAP) GetGroup(ctx context.Context, groupID string) (*libregraph.Group,
|
||||
var errmsg string
|
||||
if lerr, ok := err.(*ldap.Error); ok {
|
||||
if lerr.ResultCode == ldap.LDAPResultSizeLimitExceeded {
|
||||
errmsg = fmt.Sprintf("too many results searching for group '%s'", groupID)
|
||||
errmsg = fmt.Sprintf("too many results searching for group '%s'", nameOrID)
|
||||
i.logger.Debug().Str("backend", "ldap").Err(lerr).Msg(errmsg)
|
||||
}
|
||||
}
|
||||
@@ -393,7 +407,7 @@ func (i *LDAP) GetGroup(ctx context.Context, groupID string) (*libregraph.Group,
|
||||
return nil, errNotFound
|
||||
}
|
||||
|
||||
return i.createGroupModelFromLDAP(res.Entries[0]), nil
|
||||
return res.Entries[0], nil
|
||||
}
|
||||
|
||||
func (i *LDAP) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error) {
|
||||
@@ -436,6 +450,32 @@ func (i *LDAP) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregr
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Backend Interface for the LDAP Backend
|
||||
func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string) ([]*libregraph.User, error) {
|
||||
e, err := i.getLDAPGroupByNameOrID(groupID, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := []*libregraph.User{}
|
||||
|
||||
for _, memberDN := range e.GetEqualFoldAttributeValues(i.groupAttributeMap.member) {
|
||||
if memberDN == "" {
|
||||
continue
|
||||
}
|
||||
i.logger.Debug().Str("memberDN", memberDN).Msg("lookup")
|
||||
ue, err := i.getUserByDN(memberDN)
|
||||
if err != nil {
|
||||
// Ignore errors when reading a specific member fails, just log them and continue
|
||||
i.logger.Warn().Err(err).Str("member", memberDN).Msg("error reading group member")
|
||||
continue
|
||||
}
|
||||
result = append(result, i.createUserModelFromLDAP(ue))
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateGroup implements the Backend Interface for the LDAP Backend
|
||||
// It is currently restricted to managing groups based on the "groupOfNames" ObjectClass.
|
||||
// As "groupOfNames" requires a "member" Attribute to be present. Empty Groups (groups
|
||||
|
||||
@@ -87,3 +87,31 @@ func (g Graph) GetGroup(w http.ResponseWriter, r *http.Request) {
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, group)
|
||||
}
|
||||
|
||||
func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
groupID := chi.URLParam(r, "groupID")
|
||||
groupID, err := url.PathUnescape(groupID)
|
||||
if err != nil {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping group id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if groupID == "" {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing group id")
|
||||
return
|
||||
}
|
||||
|
||||
members, err := g.identityBackend.GetGroupMembers(r.Context(), groupID)
|
||||
if err != nil {
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, members)
|
||||
}
|
||||
|
||||
@@ -69,6 +69,11 @@ func (i instrument) PostGroup(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PostGroup(w, r)
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Service interface.
|
||||
func (i instrument) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (i instrument) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetDrives(w, r)
|
||||
|
||||
@@ -69,6 +69,11 @@ func (l logging) PostGroup(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PostGroup(w, r)
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Service interface.
|
||||
func (l logging) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (l logging) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetDrives(w, r)
|
||||
|
||||
@@ -34,6 +34,7 @@ type Service interface {
|
||||
GetGroups(http.ResponseWriter, *http.Request)
|
||||
GetGroup(http.ResponseWriter, *http.Request)
|
||||
PostGroup(http.ResponseWriter, *http.Request)
|
||||
GetGroupMembers(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetDrives(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
@@ -116,6 +117,7 @@ func NewService(opts ...Option) Service {
|
||||
r.Post("/", svc.PostGroup)
|
||||
r.Route("/{groupID}", func(r chi.Router) {
|
||||
r.Get("/", svc.GetGroup)
|
||||
r.Get("/members", svc.GetGroupMembers)
|
||||
})
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
|
||||
@@ -65,6 +65,11 @@ func (t tracing) PostGroup(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PostGroup(w, r)
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Service interface.
|
||||
func (t tracing) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (t tracing) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetDrives(w, r)
|
||||
|
||||
Reference in New Issue
Block a user