mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-05 19:09:56 -05:00
Implement adding a member to a Group
This implements POST request to the graph/v1.0/groups/{groupid}/members/$ref
endpoint. Allowing to add members to a group.
This commit is contained in:
@@ -26,6 +26,8 @@ type Backend interface {
|
||||
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)
|
||||
// AddMemberToGroup adds a new member (reference by ID) to supplied group in the identity backend.
|
||||
AddMemberToGroup(ctx context.Context, groupID string, memberID string) error
|
||||
}
|
||||
|
||||
func CreateUserModelFromCS3(u *cs3.User) *libregraph.User {
|
||||
|
||||
@@ -179,6 +179,11 @@ func (i *CS3) GetGroupMembers(ctx context.Context, groupID string) ([]*libregrap
|
||||
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
}
|
||||
|
||||
// AddMemberToGroup implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||
func (i *CS3) AddMemberToGroup(ctx context.Context, groupID string, memberID string) error {
|
||||
return errorcode.New(errorcode.NotSupported, "not implemented")
|
||||
}
|
||||
|
||||
func createGroupModelFromCS3(g *cs3group.Group) *libregraph.Group {
|
||||
if g.Id == nil {
|
||||
g.Id = &cs3group.GroupId{}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/gofrs/uuid"
|
||||
ldapdn "github.com/libregraph/idm/pkg/ldapdn"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
"github.com/owncloud/ocis/graph/pkg/config"
|
||||
@@ -551,6 +552,53 @@ func (i *LDAP) CreateGroup(ctx context.Context, group libregraph.Group) (*libreg
|
||||
return i.createGroupModelFromLDAP(e), nil
|
||||
}
|
||||
|
||||
// AddMemberToGroup implements the Backend Interface for the LDAP backend.
|
||||
// Currently it is limited to adding Users as Group members. Adding other groups
|
||||
// as members is not yet implemented
|
||||
func (i *LDAP) AddMemberToGroup(ctx context.Context, groupID string, memberID string) error {
|
||||
ge, err := i.getLDAPGroupByID(groupID, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
me, err := i.getLDAPUserByID(memberID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.logger.Debug().Str("backend", "ldap").Str("groupdn", ge.DN).Str("member", me.DN).Msg("Add Member")
|
||||
|
||||
mr := ldap.ModifyRequest{DN: ge.DN}
|
||||
// Handle empty groups (using the empty member attribute)
|
||||
current := ge.GetEqualFoldAttributeValues(i.groupAttributeMap.member)
|
||||
if len(current) == 1 && current[0] == "" {
|
||||
mr.Delete(i.groupAttributeMap.member, []string{""})
|
||||
}
|
||||
nUserDN, err := ldapdn.ParseNormalize(me.DN)
|
||||
for _, member := range current {
|
||||
if member == "" {
|
||||
continue
|
||||
}
|
||||
if nMember, err := ldapdn.ParseNormalize(member); err != nil {
|
||||
// We couldn't parse the member value as a DN. Let's keep it
|
||||
// as it is but log a warning
|
||||
i.logger.Warn().Str("memberDN", member).Err(err).Msg("Couldn't parse DN")
|
||||
continue
|
||||
} else {
|
||||
if nMember == nUserDN {
|
||||
i.logger.Info().Str("memberDN", member).Msg("User already present. Nothing to do")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mr.Add(i.groupAttributeMap.member, []string{me.DN})
|
||||
|
||||
if err := i.conn.Modify(&mr); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *LDAP) createUserModelFromLDAP(e *ldap.Entry) *libregraph.User {
|
||||
if e == nil {
|
||||
return nil
|
||||
@@ -569,6 +617,7 @@ func (i *LDAP) createGroupModelFromLDAP(e *ldap.Entry) *libregraph.Group {
|
||||
Id: pointerOrNil(e.GetEqualFoldAttributeValue(i.groupAttributeMap.id)),
|
||||
}
|
||||
}
|
||||
|
||||
func pointerOrNil(val string) *string {
|
||||
if val == "" {
|
||||
return nil
|
||||
|
||||
@@ -5,13 +5,13 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
//msgraph "github.com/owncloud/open-graph-api-go" // FIXME add groups to open graph, needs OnPremisesSamAccountName and OnPremisesDomainName
|
||||
)
|
||||
|
||||
// GetGroups implements the Service interface.
|
||||
@@ -115,3 +115,64 @@ func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, members)
|
||||
}
|
||||
|
||||
// PostGroupMember implements the Service interface.
|
||||
func (g Graph) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
g.logger.Info().Msg("Calling PostGroupMember")
|
||||
|
||||
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
|
||||
}
|
||||
memberRef := libregraph.NewMemberReference()
|
||||
err = json.NewDecoder(r.Body).Decode(memberRef)
|
||||
if err != nil {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
memberRefURL, ok := memberRef.GetOdataIdOk()
|
||||
if !ok {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusInternalServerError, "@odata.id refernce is missing")
|
||||
return
|
||||
}
|
||||
memberURL, err := url.ParseRequestURI(*memberRefURL)
|
||||
if err != nil {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusInternalServerError, "Error parsing @odata.id url")
|
||||
return
|
||||
}
|
||||
segments := strings.Split(memberURL.Path, "/")
|
||||
if len(segments) < 2 {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusInternalServerError, "Error parsing @odata.id url path")
|
||||
return
|
||||
}
|
||||
id := segments[len(segments)-1]
|
||||
memberType := segments[len(segments)-2]
|
||||
// The MS Graph spec allows "directoryObject", "user", "group" and "organizational Contact"
|
||||
// we restrict this to users for now. Might add Groups as members later
|
||||
if memberType != "users" {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusInternalServerError, "Only user are allowed as group members")
|
||||
return
|
||||
}
|
||||
|
||||
g.logger.Debug().Str("memberType", memberType).Str("id", id).Msg("Add Member")
|
||||
err = g.identityBackend.AddMemberToGroup(r.Context(), groupID, id)
|
||||
|
||||
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.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ func (i instrument) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// PostGroupMember implements the Service interface.
|
||||
func (i instrument) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PostGroupMember(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (i instrument) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetDrives(w, r)
|
||||
|
||||
@@ -74,6 +74,11 @@ func (l logging) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// PostGroupMember implements the Service interface.
|
||||
func (l logging) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PostGroupMember(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (l logging) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetDrives(w, r)
|
||||
|
||||
@@ -35,6 +35,7 @@ type Service interface {
|
||||
GetGroup(http.ResponseWriter, *http.Request)
|
||||
PostGroup(http.ResponseWriter, *http.Request)
|
||||
GetGroupMembers(http.ResponseWriter, *http.Request)
|
||||
PostGroupMember(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetDrives(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
@@ -117,7 +118,10 @@ 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.Route("/members", func(r chi.Router) {
|
||||
r.Get("/", svc.GetGroupMembers)
|
||||
r.Post("/$ref", svc.PostGroupMember)
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
|
||||
@@ -70,6 +70,11 @@ func (t tracing) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetGroupMembers(w, r)
|
||||
}
|
||||
|
||||
// PostGroupMember implements the Service interface.
|
||||
func (t tracing) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PostGroupMember(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