graph: Groups convert CS3 code to its own backend

Until the LDAP backend is ready the existing CS3 code should keep
working.

This also adds the initial stubs for the upcoming LDAP Backend.
This commit is contained in:
Ralf Haferkamp
2021-11-25 14:51:52 +01:00
parent 76390f3d4f
commit f253f7bc8e
6 changed files with 123 additions and 116 deletions
-9
View File
@@ -29,15 +29,6 @@ func (g Graph) GetClient() (gateway.GatewayAPIClient, error) {
return pool.GetGatewayServiceClient(g.config.Reva.Address)
}
// The key type is unexported to prevent collisions with context keys defined in
// other packages.
type key int
const (
userKey key = iota
groupKey
)
type listResponse struct {
Value interface{} `json:"value,omitempty"`
}
+24 -103
View File
@@ -1,129 +1,50 @@
package svc
import (
"context"
"errors"
"net/http"
cs3 "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"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
msgraph "github.com/yaegashi/msgraph.go/v1.0"
)
// GroupCtx middleware is used to load an User object from
// the URL parameters passed through as the request. In case
// the User could not be found, we stop here and return a 404.
func (g Graph) GroupCtx(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
groupID := chi.URLParam(r, "groupID")
if groupID == "" {
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing group id")
return
}
client, err := g.GetClient()
if err != nil {
g.logger.Error().Err(err).Msg("could not get client")
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
res, err := client.GetGroupByClaim(r.Context(), &cs3.GetGroupByClaimRequest{
Claim: "groupid", // FIXME add consts to reva
Value: groupID,
})
switch {
case err != nil:
g.logger.Error().Err(err).Str("groupid", groupID).Msg("error sending get group by claim id grpc request")
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
return
case res.Status.Code != cs3rpc.Code_CODE_OK:
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
return
}
g.logger.Error().Err(err).Str("groupid", groupID).Msg("error sending get group by claim id grpc request")
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
return
}
ctx := context.WithValue(r.Context(), groupKey, res.Group)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// GetGroups implements the Service interface.
func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
client, err := g.GetClient()
groups, err := g.identityBackend.GetGroups(r.Context(), r.URL.Query())
if err != nil {
g.logger.Error().Err(err).Msg("could not get client")
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
search := r.URL.Query().Get("search")
if search == "" {
search = r.URL.Query().Get("$search")
}
res, err := client.FindGroups(r.Context(), &cs3.FindGroupsRequest{
// FIXME presence match is currently not implemented, an empty search currently leads to
// Unwilling To Perform": Search Error: error parsing filter: (&(objectclass=posixAccount)(|(cn=*)(displayname=*)(mail=*))), error: Present filter match for cn not implemented
Filter: search,
})
switch {
case err != nil:
g.logger.Error().Err(err).Str("search", search).Msg("error sending find groups grpc request")
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, err.Error())
return
case res.Status.Code != cs3rpc.Code_CODE_OK:
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, res.Status.Message)
return
var errcode errorcode.Error
if errors.As(err, &errcode) {
errcode.Render(w, r)
} else {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
}
g.logger.Error().Err(err).Str("search", search).Msg("error sending find groups grpc request")
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
return
}
groups := make([]*msgraph.Group, 0, len(res.Groups))
for _, group := range res.Groups {
groups = append(groups, createGroupModelFromCS3(group))
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &listResponse{Value: groups})
}
// GetGroup implements the Service interface.
func (g Graph) GetGroup(w http.ResponseWriter, r *http.Request) {
group := r.Context().Value(groupKey).(*cs3.Group)
groupID := chi.URLParam(r, "groupID")
if groupID == "" {
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing group id")
return
}
group, err := g.identityBackend.GetGroup(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())
}
}
render.Status(r, http.StatusOK)
render.JSON(w, r, createGroupModelFromCS3(group))
}
func createGroupModelFromCS3(g *cs3.Group) *msgraph.Group {
if g.Id == nil {
g.Id = &cs3.GroupId{}
}
return &msgraph.Group{
DirectoryObject: msgraph.DirectoryObject{
Entity: msgraph.Entity{
ID: &g.Id.OpaqueId,
},
},
OnPremisesDomainName: &g.Id.Idp,
OnPremisesSamAccountName: &g.GroupName,
DisplayName: &g.DisplayName,
Mail: &g.Mail,
// TODO when to fetch and expand memberof, usernames or ids?
}
render.JSON(w, r, group)
}
-1
View File
@@ -62,7 +62,6 @@ func NewService(opts ...Option) Service {
r.Route("/groups", func(r chi.Router) {
r.Get("/", svc.GetGroups)
r.Route("/{groupID}", func(r chi.Router) {
r.Use(svc.GroupCtx)
r.Get("/", svc.GetGroup)
})
})