graph: Pass parsed odata request to the identity backend

In preparation for some more advanced queries pass the parse odata request
tVo the identity backend methods instead of the raw url.Values{}. This also
add some helpers for validating $expand and $search queries to reject
some unsupported queries.

Also remove support for `$select=memberOf` and `$select=drive|drives` queries
and stick to the technically correct `$expand=...`.
This commit is contained in:
Ralf Haferkamp
2023-02-08 14:25:55 +01:00
committed by Ralf Haferkamp
parent 25d2a2bc71
commit 26f7523ff8
8 changed files with 177 additions and 75 deletions
+3 -1
View File
@@ -322,6 +322,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, newDrive)
}
// UpdateDrive updates the properties of a storage drive (space).
func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling update drive")
@@ -594,7 +595,7 @@ func (g Graph) cs3StorageSpaceToDrive(ctx context.Context, baseURL *url.URL, spa
} else {
var user libregraph.User
if item := g.usersCache.Get(id); item == nil {
if requestedUser, err := g.identityBackend.GetUser(ctx, id, url.Values{}); err == nil {
if requestedUser, err := g.identityBackend.GetUser(ctx, id, &godata.GoDataRequest{}); err == nil {
user = *requestedUser
g.usersCache.Set(id, user, ttlcache.DefaultTTL)
}
@@ -881,6 +882,7 @@ func listStorageSpacesTypeFilter(spaceType string) *storageprovider.ListStorageS
}
}
// DeleteDrive deletes a storage drive (space).
func (g Graph) DeleteDrive(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling delete drive")
+55 -11
View File
@@ -32,6 +32,14 @@ import (
func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling get user in /me")
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: query error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
u, ok := revactx.ContextGetUser(r.Context())
if !ok {
@@ -39,7 +47,14 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "user not in context")
return
}
exp := strings.Split(r.URL.Query().Get("$expand"), ",")
exp, err := identity.GetExpandValues(odataReq.Query)
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
var me *libregraph.User
// We can just return the user from context unless we need to expand the group memberships
if !slices.Contains(exp, "memberOf") {
@@ -47,7 +62,7 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
} else {
var err error
logger.Debug().Msg("calling get user on backend")
me, err = g.identityBackend.GetUser(r.Context(), u.GetId().GetOpaqueId(), r.URL.Query())
me, err = g.identityBackend.GetUser(r.Context(), u.GetId().GetOpaqueId(), odataReq)
if err != nil {
logger.Debug().Err(err).Interface("user", u).Msg("could not get user from backend")
var errcode errorcode.Error
@@ -111,7 +126,7 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
}
logger.Debug().Interface("query", r.URL.Query()).Msg("calling get users on backend")
users, err := g.identityBackend.GetUsers(r.Context(), r.URL.Query())
users, err := g.identityBackend.GetUsers(r.Context(), odataReq)
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users from backend")
var errcode errorcode.Error
@@ -123,8 +138,12 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
return
}
// expand appRoleAssignments if requested
exp := strings.Split(r.URL.Query().Get("$expand"), ",")
exp, err := identity.GetExpandValues(odataReq.Query)
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
expandAppRoleAssignments := slices.Contains(exp, "appRoleAssignments")
expandMemberOf := slices.Contains(exp, "memberOf")
for _, u := range users {
@@ -240,6 +259,9 @@ func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling get user")
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
userID := chi.URLParam(r, "userID")
userID, err := url.PathUnescape(userID)
if err != nil {
@@ -254,8 +276,22 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
return
}
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: query error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
exp, err := identity.GetExpandValues(odataReq.Query)
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
logger.Debug().Str("id", userID).Msg("calling get user from backend")
user, err := g.identityBackend.GetUser(r.Context(), userID, r.URL.Query())
user, err := g.identityBackend.GetUser(r.Context(), userID, odataReq)
if err != nil {
logger.Debug().Err(err).Msg("could not get user: error fetching user from backend")
@@ -267,10 +303,9 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
}
return
}
sel := strings.Split(r.URL.Query().Get("$select"), ",")
exp := strings.Split(r.URL.Query().Get("$expand"), ",")
listDrives := slices.Contains(sel, "drives") || slices.Contains(exp, "drives")
listDrive := slices.Contains(sel, "drive") || slices.Contains(exp, "drive")
listDrives := slices.Contains(exp, "drives")
listDrive := slices.Contains(exp, "drive")
// do we need to list all or only the personal drive
filters := []*storageprovider.ListStorageSpacesRequest_Filter{}
@@ -368,6 +403,7 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling delete user")
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
userID := chi.URLParam(r, "userID")
userID, err := url.PathUnescape(userID)
if err != nil {
@@ -381,8 +417,16 @@ func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) {
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing user id")
return
}
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
if err != nil {
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: query error")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
return
}
logger.Debug().Str("id", userID).Msg("calling get user on user backend")
user, err := g.identityBackend.GetUser(r.Context(), userID, r.URL.Query())
user, err := g.identityBackend.GetUser(r.Context(), userID, odataReq)
if err != nil {
logger.Debug().Err(err).Str("userID", userID).Msg("failed to get user from backend")
var errcode errorcode.Error