mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-22 11:09:02 -05:00
handle /education/user
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
committed by
Ralf Haferkamp
parent
962ae09bf1
commit
57fd00d238
@@ -42,11 +42,20 @@ type EducationBackend interface {
|
||||
GetSchool(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationSchool, error)
|
||||
// GetSchools lists all schools
|
||||
GetSchools(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationSchool, error)
|
||||
GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.User, error)
|
||||
GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.EducationUser, error)
|
||||
// AddMembersToSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
||||
AddMembersToSchool(ctx context.Context, schoolID string, memberID []string) error
|
||||
// RemoveMemberFromSchool removes a single member (by ID) from a school
|
||||
RemoveMemberFromSchool(ctx context.Context, schoolID string, memberID string) error
|
||||
|
||||
// CreateEducationUser creates a given education user in the identity backend.
|
||||
CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error)
|
||||
// DeleteEducationUser deletes a given educationuser, identified by username or id, from the backend
|
||||
DeleteEducationUser(ctx context.Context, nameOrID string) error
|
||||
// UpdateEducationUser applies changes to given education user, identified by username or id
|
||||
UpdateEducationUser(ctx context.Context, nameOrID string, user libregraph.EducationUser) (*libregraph.EducationUser, error)
|
||||
GetEducationUser(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationUser, error)
|
||||
GetEducationUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationUser, error)
|
||||
}
|
||||
|
||||
func CreateUserModelFromCS3(u *cs3.User) *libregraph.User {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
)
|
||||
|
||||
// CreateEducationUser creates a given education user in the identity backend.
|
||||
func (i *LDAP) CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// DeleteEducationUser deletes a given educationuser, identified by username or id, from the backend
|
||||
func (i *LDAP) DeleteEducationUser(ctx context.Context, nameOrID string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// UpdateEducationUser applies changes to given education user, identified by username or id
|
||||
func (i *LDAP) UpdateEducationUser(ctx context.Context, nameOrID string, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetEducationUser implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetEducationUser(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetEducationUsers implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetEducationUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
@@ -177,7 +177,7 @@ func (i *LDAP) GetSchools(ctx context.Context, queryParam url.Values) ([]*libreg
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.User, error) {
|
||||
func (i *LDAP) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,29 @@ func (_m *EducationBackend) AddMembersToSchool(ctx context.Context, schoolID str
|
||||
return r0
|
||||
}
|
||||
|
||||
// CreateEducationUser provides a mock function with given fields: ctx, user
|
||||
func (_m *EducationBackend) CreateEducationUser(ctx context.Context, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
|
||||
ret := _m.Called(ctx, user)
|
||||
|
||||
var r0 *libregraph.EducationUser
|
||||
if rf, ok := ret.Get(0).(func(context.Context, libregraph.EducationUser) *libregraph.EducationUser); ok {
|
||||
r0 = rf(ctx, user)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*libregraph.EducationUser)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, libregraph.EducationUser) error); ok {
|
||||
r1 = rf(ctx, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateSchool provides a mock function with given fields: ctx, group
|
||||
func (_m *EducationBackend) CreateSchool(ctx context.Context, group libregraph.EducationSchool) (*libregraph.EducationSchool, error) {
|
||||
ret := _m.Called(ctx, group)
|
||||
@@ -54,6 +77,20 @@ func (_m *EducationBackend) CreateSchool(ctx context.Context, group libregraph.E
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteEducationUser provides a mock function with given fields: ctx, nameOrID
|
||||
func (_m *EducationBackend) DeleteEducationUser(ctx context.Context, nameOrID string) error {
|
||||
ret := _m.Called(ctx, nameOrID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(ctx, nameOrID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteSchool provides a mock function with given fields: ctx, id
|
||||
func (_m *EducationBackend) DeleteSchool(ctx context.Context, id string) error {
|
||||
ret := _m.Called(ctx, id)
|
||||
@@ -68,6 +105,52 @@ func (_m *EducationBackend) DeleteSchool(ctx context.Context, id string) error {
|
||||
return r0
|
||||
}
|
||||
|
||||
// GetEducationUser provides a mock function with given fields: ctx, nameOrID, queryParam
|
||||
func (_m *EducationBackend) GetEducationUser(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationUser, error) {
|
||||
ret := _m.Called(ctx, nameOrID, queryParam)
|
||||
|
||||
var r0 *libregraph.EducationUser
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, url.Values) *libregraph.EducationUser); ok {
|
||||
r0 = rf(ctx, nameOrID, queryParam)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*libregraph.EducationUser)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, url.Values) error); ok {
|
||||
r1 = rf(ctx, nameOrID, queryParam)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetEducationUsers provides a mock function with given fields: ctx, queryParam
|
||||
func (_m *EducationBackend) GetEducationUsers(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationUser, error) {
|
||||
ret := _m.Called(ctx, queryParam)
|
||||
|
||||
var r0 []*libregraph.EducationUser
|
||||
if rf, ok := ret.Get(0).(func(context.Context, url.Values) []*libregraph.EducationUser); ok {
|
||||
r0 = rf(ctx, queryParam)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*libregraph.EducationUser)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, url.Values) error); ok {
|
||||
r1 = rf(ctx, queryParam)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetSchool provides a mock function with given fields: ctx, nameOrID, queryParam
|
||||
func (_m *EducationBackend) GetSchool(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationSchool, error) {
|
||||
ret := _m.Called(ctx, nameOrID, queryParam)
|
||||
@@ -92,15 +175,15 @@ func (_m *EducationBackend) GetSchool(ctx context.Context, nameOrID string, quer
|
||||
}
|
||||
|
||||
// GetSchoolMembers provides a mock function with given fields: ctx, id
|
||||
func (_m *EducationBackend) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.User, error) {
|
||||
func (_m *EducationBackend) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 []*libregraph.User
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.User); ok {
|
||||
var r0 []*libregraph.EducationUser
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.EducationUser); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*libregraph.User)
|
||||
r0 = ret.Get(0).([]*libregraph.EducationUser)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +234,29 @@ func (_m *EducationBackend) RemoveMemberFromSchool(ctx context.Context, schoolID
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateEducationUser provides a mock function with given fields: ctx, nameOrID, user
|
||||
func (_m *EducationBackend) UpdateEducationUser(ctx context.Context, nameOrID string, user libregraph.EducationUser) (*libregraph.EducationUser, error) {
|
||||
ret := _m.Called(ctx, nameOrID, user)
|
||||
|
||||
var r0 *libregraph.EducationUser
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, libregraph.EducationUser) *libregraph.EducationUser); ok {
|
||||
r0 = rf(ctx, nameOrID, user)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*libregraph.EducationUser)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string, libregraph.EducationUser) error); ok {
|
||||
r1 = rf(ctx, nameOrID, user)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewEducationBackend interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/cs3org/reva/v2/pkg/utils"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
settingssvc "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// GetEducationUsers implements the Service interface.
|
||||
func (g Graph) GetEducationUsers(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling get education users")
|
||||
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 education users: query error")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Interface("query", r.URL.Query()).Msg("calling get education users on backend")
|
||||
users, err := g.identityEducationBackend.GetEducationUsers(r.Context(), r.URL.Query())
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get education users from backend")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
users, err = sortEducationUsers(odataReq, users)
|
||||
if err != nil {
|
||||
logger.Debug().Interface("query", odataReq).Msg("error while sorting education users according to query")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: users})
|
||||
}
|
||||
|
||||
// PostEducationUser implements the Service interface.
|
||||
func (g Graph) PostEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("body", r.Body).Msg("calling create education user")
|
||||
u := libregraph.NewEducationUser()
|
||||
err := json.NewDecoder(r.Body).Decode(u)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not create education user: invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// Disallow user-supplied IDs. It's supposed to be readonly. We're either
|
||||
// generating them in the backend ourselves or rely on the Backend's
|
||||
// storage (e.g. LDAP) to provide a unique ID.
|
||||
if _, ok := u.GetIdOk(); ok {
|
||||
logger.Debug().Interface("user", u).Msg("could not create education user: id is a read-only attribute")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "education user id is a read-only attribute")
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := u.GetDisplayNameOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing required Attribute: 'displayName'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'displayName'")
|
||||
return
|
||||
}
|
||||
|
||||
identities, ok := u.GetIdentitiesOk()
|
||||
if !ok {
|
||||
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing required Collection: 'identities'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'identities'")
|
||||
return
|
||||
}
|
||||
if len(identities) < 1 {
|
||||
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing entry in Collection: 'identities'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Collection: 'identities'")
|
||||
return
|
||||
}
|
||||
for i, identity := range identities {
|
||||
if _, ok := identity.GetIssuerOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("user", u).Msgf("could not create education user: missing Attribute in 'identities' Collection Entry %d: 'issuer'", i)
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("missing Attribute in 'identities' Collection Entry %d: 'issuer'", i))
|
||||
return
|
||||
}
|
||||
if _, ok := identity.GetIssuerAssignedIdOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("user", u).Msgf("could not create education user: missing Attribute in 'identities' Collection Entry %d: 'issuerAssignedId'", i)
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("missing Attribute in 'identities' Collection Entry %d: 'issuerAssignedId'", i))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if accountName, ok := u.GetOnPremisesSamAccountNameOk(); ok {
|
||||
if !isValidUsername(*accountName) {
|
||||
logger.Debug().Str("username", *accountName).Msg("could not create education user: username must be at least the local part of an email")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("username %s must be at least the local part of an email", *u.OnPremisesSamAccountName))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.Debug().Interface("user", u).Msg("could not create education user: missing required Attribute: 'onPremisesSamAccountName'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'onPremisesSamAccountName'")
|
||||
return
|
||||
}
|
||||
|
||||
if mail, ok := u.GetMailOk(); ok {
|
||||
if !isValidEmail(*mail) {
|
||||
logger.Debug().Str("mail", *u.Mail).Msg("could not create education user: invalid email address")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("%v is not a valid email address", *u.Mail))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
logger.Debug().Interface("user", u).Msg("could not create education user: missing required Attribute: 'mail'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'mail'")
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := u.GetPrimaryRoleOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("user", u).Msg("could not create education user: missing required Attribute: 'primaryRole'")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing required Attribute: 'primaryRole'")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Interface("user", u).Msg("calling create education user on backend")
|
||||
if u, err = g.identityEducationBackend.CreateEducationUser(r.Context(), *u); err != nil {
|
||||
logger.Debug().Err(err).Msg("could not create education user: backend error")
|
||||
var ecErr errorcode.Error
|
||||
if errors.As(err, &ecErr) {
|
||||
ecErr.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// assign roles if possible
|
||||
if g.roleService != nil {
|
||||
// All users get the user role by default currently.
|
||||
// to all new users for now, as create Account request does not have any role field
|
||||
if _, err = g.roleService.AssignRoleToUser(r.Context(), &settings.AssignRoleToUserRequest{
|
||||
AccountUuid: *u.Id,
|
||||
RoleId: settingssvc.BundleUUIDRoleUser,
|
||||
}); err != nil {
|
||||
// log as error, admin eventually needs to do something
|
||||
logger.Error().Err(err).Str("id", *u.Id).Str("role", settingssvc.BundleUUIDRoleUser).Msg("could not create education user: role assignment failed")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "role assignment failed")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
e := events.UserCreated{UserID: *u.Id}
|
||||
if currentUser, ok := revactx.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, u)
|
||||
}
|
||||
|
||||
// GetEducationUser implements the Service interface.
|
||||
func (g Graph) GetEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling get education user")
|
||||
userID := chi.URLParam(r, "userID")
|
||||
userID, err := url.PathUnescape(userID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", userID).Msg("could not get education user: unescaping education user id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping education user id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if userID == "" {
|
||||
logger.Debug().Msg("could not get user: missing education user id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing education user id")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("id", userID).Msg("calling get education user from backend")
|
||||
user, err := g.identityEducationBackend.GetEducationUser(r.Context(), userID, r.URL.Query())
|
||||
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get education user: error fetching education user from backend")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
sel := strings.Split(r.URL.Query().Get("$select"), ",")
|
||||
exp := strings.Split(r.URL.Query().Get("$expand"), ",")
|
||||
if slices.Contains(sel, "drive") || slices.Contains(sel, "drives") || slices.Contains(exp, "drive") || slices.Contains(exp, "drives") {
|
||||
wdu, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
// log error, wrong configuration
|
||||
logger.Error().
|
||||
Err(err).
|
||||
Str("webdav_base", g.config.Spaces.WebDavBase).
|
||||
Str("webdav_path", g.config.Spaces.WebDavPath).
|
||||
Msg("error parsing webdav URL")
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
logger.Debug().Str("id", user.GetId()).Msg("calling list storage spaces with education user id filter")
|
||||
f := listStorageSpacesUserFilter(user.GetId())
|
||||
// use the unrestricted flag to get all possible spaces
|
||||
// users with the canListAllSpaces permission should see all spaces
|
||||
opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T")
|
||||
lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{
|
||||
Opaque: opaque,
|
||||
Filters: []*storageprovider.ListStorageSpacesRequest_Filter{f},
|
||||
})
|
||||
if err != nil {
|
||||
// transport error, needs to be fixed by admin
|
||||
logger.Error().Err(err).Interface("query", r.URL.Query()).Msg("error getting storages: transport error")
|
||||
render.Status(r, http.StatusInternalServerError)
|
||||
render.JSON(w, r, user)
|
||||
return
|
||||
}
|
||||
if lspr.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
logger.Debug().Str("grpc", lspr.GetStatus().GetMessage()).Msg("could not get drive for education user")
|
||||
// in case of NOT_OK, we can just return the user object with empty drives
|
||||
render.Status(r, status.HTTPStatusFromCode(http.StatusOK))
|
||||
render.JSON(w, r, user)
|
||||
return
|
||||
}
|
||||
drives := []libregraph.Drive{}
|
||||
for _, sp := range lspr.GetStorageSpaces() {
|
||||
d, err := g.cs3StorageSpaceToDrive(r.Context(), wdu, sp)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("id", sp.Id).Msg("error converting space to drive")
|
||||
continue
|
||||
}
|
||||
quota, err := g.getDriveQuota(r.Context(), sp)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("id", sp.Id).Msg("error calling get quota on drive")
|
||||
}
|
||||
d.Quota = quota
|
||||
if slices.Contains(sel, "drive") || slices.Contains(exp, "drive") {
|
||||
if *d.DriveType == "personal" {
|
||||
user.Drive = d
|
||||
}
|
||||
} else {
|
||||
drives = append(drives, *d)
|
||||
user.Drives = drives
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, user)
|
||||
}
|
||||
|
||||
// DeleteEducationUser implements the Service interface.
|
||||
func (g Graph) DeleteEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling delete education user")
|
||||
userID := chi.URLParam(r, "userID")
|
||||
userID, err := url.PathUnescape(userID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", userID).Msg("could not delete education user: unescaping education user id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping education user id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if userID == "" {
|
||||
logger.Debug().Msg("could not delete education user: missing education user id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing education user id")
|
||||
return
|
||||
}
|
||||
logger.Debug().Str("id", userID).Msg("calling get education user on user backend")
|
||||
user, err := g.identityEducationBackend.GetEducationUser(r.Context(), userID, r.URL.Query())
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("userID", userID).Msg("failed to get education user from backend")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
e := events.UserDeleted{UserID: user.GetId()}
|
||||
if currentUser, ok := revactx.ContextGetUser(r.Context()); ok {
|
||||
if currentUser.GetId().GetOpaqueId() == user.GetId() {
|
||||
logger.Debug().Msg("could not delete education user: self deletion forbidden")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusForbidden, "self deletion forbidden")
|
||||
return
|
||||
}
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
|
||||
if g.gatewayClient != nil {
|
||||
logger.Debug().
|
||||
Str("user", user.GetId()).
|
||||
Msg("calling list spaces with user filter to fetch the personal space for deletion")
|
||||
opaque := utils.AppendPlainToOpaque(nil, "unrestricted", "T")
|
||||
f := listStorageSpacesUserFilter(user.GetId())
|
||||
lspr, err := g.gatewayClient.ListStorageSpaces(r.Context(), &storageprovider.ListStorageSpacesRequest{
|
||||
Opaque: opaque,
|
||||
Filters: []*storageprovider.ListStorageSpacesRequest_Filter{f},
|
||||
})
|
||||
if err != nil {
|
||||
// transport error, log as error
|
||||
logger.Error().Err(err).Msg("could not fetch spaces: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not fetch spaces for deletion, aborting")
|
||||
return
|
||||
}
|
||||
for _, sp := range lspr.GetStorageSpaces() {
|
||||
if !(sp.SpaceType == "personal" && sp.Owner.Id.OpaqueId == user.GetId()) {
|
||||
continue
|
||||
}
|
||||
// TODO: check if request contains a homespace and if, check if requesting user has the privilege to
|
||||
// delete it and make sure it is not deleting its own homespace
|
||||
// needs modification of the cs3api
|
||||
|
||||
// Deleting a space a two step process (1. disabling/trashing, 2. purging)
|
||||
// Do the "disable/trash" step only if the space is not marked as trashed yet:
|
||||
if _, ok := sp.Opaque.Map["trashed"]; !ok {
|
||||
_, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{
|
||||
Id: &storageprovider.StorageSpaceId{
|
||||
OpaqueId: sp.Id.OpaqueId,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not disable homespace: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not disable homespace, aborting")
|
||||
return
|
||||
}
|
||||
}
|
||||
purgeFlag := utils.AppendPlainToOpaque(nil, "purge", "")
|
||||
_, err := g.gatewayClient.DeleteStorageSpace(r.Context(), &storageprovider.DeleteStorageSpaceRequest{
|
||||
Opaque: purgeFlag,
|
||||
Id: &storageprovider.StorageSpaceId{
|
||||
OpaqueId: sp.Id.OpaqueId,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
// transport error, log as error
|
||||
logger.Error().Err(err).Msg("could not delete homespace: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not delete homespace, aborting")
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug().Str("id", user.GetId()).Msg("calling delete education user on backend")
|
||||
err = g.identityEducationBackend.DeleteEducationUser(r.Context(), user.GetId())
|
||||
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not delete education user: backend error")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
// PatchEducationUser implements the Service Interface. Updates the specified attributes of an
|
||||
// ExistingUser
|
||||
func (g Graph) PatchEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling patch education user")
|
||||
nameOrID := chi.URLParam(r, "userID")
|
||||
nameOrID, err := url.PathUnescape(nameOrID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", nameOrID).Msg("could not update education user: unescaping education user id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping education user id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if nameOrID == "" {
|
||||
logger.Debug().Msg("could not update education user: missing education user id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing education user id")
|
||||
return
|
||||
}
|
||||
changes := libregraph.NewEducationUser()
|
||||
err = json.NewDecoder(r.Body).Decode(changes)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not update education user: invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest,
|
||||
fmt.Sprintf("invalid request body: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
var features []events.UserFeature
|
||||
if mail, ok := changes.GetMailOk(); ok {
|
||||
if !isValidEmail(*mail) {
|
||||
logger.Debug().Str("mail", *mail).Msg("could not update education user: email is not a valid email address")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest,
|
||||
fmt.Sprintf("'%s' is not a valid email address", *mail))
|
||||
return
|
||||
}
|
||||
features = append(features, events.UserFeature{Name: "email", Value: *mail})
|
||||
}
|
||||
|
||||
if name, ok := changes.GetDisplayNameOk(); ok {
|
||||
features = append(features, events.UserFeature{Name: "displayname", Value: *name})
|
||||
}
|
||||
|
||||
logger.Debug().Str("nameid", nameOrID).Interface("changes", *changes).Msg("calling update education user on backend")
|
||||
u, err := g.identityEducationBackend.UpdateEducationUser(r.Context(), nameOrID, *changes)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", nameOrID).Msg("could not update education user: backend error")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
e := events.UserFeatureChanged{
|
||||
UserID: nameOrID,
|
||||
Features: features,
|
||||
}
|
||||
if currentUser, ok := revactx.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusOK) // TODO StatusNoContent when prefer=minimal is used
|
||||
render.JSON(w, r, u)
|
||||
|
||||
}
|
||||
|
||||
func sortEducationUsers(req *godata.GoDataRequest, users []*libregraph.EducationUser) ([]*libregraph.EducationUser, error) {
|
||||
var sorter sort.Interface
|
||||
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
|
||||
return users, nil
|
||||
}
|
||||
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
|
||||
case displayNameAttr:
|
||||
sorter = educationUsersByDisplayName{users}
|
||||
case "mail":
|
||||
sorter = educationUsersByMail{users}
|
||||
case "onPremisesSamAccountName":
|
||||
sorter = educationUsersByOnPremisesSamAccountName{users}
|
||||
default:
|
||||
return nil, fmt.Errorf("we do not support <%s> as a order parameter", req.Query.OrderBy.OrderByItems[0].Field.Value)
|
||||
}
|
||||
|
||||
if req.Query.OrderBy.OrderByItems[0].Order == "desc" {
|
||||
sorter = sort.Reverse(sorter)
|
||||
}
|
||||
sort.Sort(sorter)
|
||||
return users, nil
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
package svc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/go-chi/chi/v5"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/mocks"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
|
||||
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
|
||||
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
|
||||
)
|
||||
|
||||
type educationUserList struct {
|
||||
Value []*libregraph.EducationUser
|
||||
}
|
||||
|
||||
var _ = Describe("EducationUsers", func() {
|
||||
var (
|
||||
svc service.Service
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
gatewayClient *mocks.GatewayClient
|
||||
eventsPublisher mocks.Publisher
|
||||
roleService *mocks.RoleService
|
||||
identityEducationBackend *identitymocks.EducationBackend
|
||||
|
||||
rr *httptest.ResponseRecorder
|
||||
|
||||
currentUser = &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "user",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
identityEducationBackend = &identitymocks.EducationBackend{}
|
||||
roleService = &mocks.RoleService{}
|
||||
gatewayClient = &mocks.GatewayClient{}
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
ctx = context.Background()
|
||||
|
||||
cfg = defaults.FullDefaultConfig()
|
||||
cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
|
||||
cfg.TokenManager.JWTSecret = "loremipsum"
|
||||
cfg.Commons = &shared.Commons{}
|
||||
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
|
||||
|
||||
_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
|
||||
svc = service.NewService(
|
||||
service.Config(cfg),
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
service.WithIdentityEducationBackend(identityEducationBackend),
|
||||
//service.WithRoleService(roleService),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("GetEducationUsers", func() {
|
||||
It("handles invalid requests", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users?$invalid=true", nil)
|
||||
svc.GetEducationUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("lists the users", func() {
|
||||
user := &libregraph.EducationUser{}
|
||||
user.SetId("user1")
|
||||
users := []*libregraph.EducationUser{user}
|
||||
|
||||
identityEducationBackend.On("GetEducationUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users", nil)
|
||||
svc.GetEducationUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := educationUserList{}
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(res.Value)).To(Equal(1))
|
||||
Expect(res.Value[0].GetId()).To(Equal("user1"))
|
||||
})
|
||||
|
||||
It("sorts", func() {
|
||||
user := &libregraph.EducationUser{}
|
||||
user.SetId("user1")
|
||||
user.SetMail("z@example.com")
|
||||
user.SetDisplayName("9")
|
||||
user.SetOnPremisesSamAccountName("9")
|
||||
user2 := &libregraph.EducationUser{}
|
||||
user2.SetId("user2")
|
||||
user2.SetMail("a@example.com")
|
||||
user2.SetDisplayName("1")
|
||||
user2.SetOnPremisesSamAccountName("1")
|
||||
users := []*libregraph.EducationUser{user, user2}
|
||||
|
||||
identityEducationBackend.On("GetEducationUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
|
||||
|
||||
getUsers := func(path string) []*libregraph.EducationUser {
|
||||
r := httptest.NewRequest(http.MethodGet, path, nil)
|
||||
rec := httptest.NewRecorder()
|
||||
svc.GetEducationUsers(rec, r)
|
||||
|
||||
Expect(rec.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rec.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := educationUserList{}
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
return res.Value
|
||||
}
|
||||
|
||||
unsorted := getUsers("/graph/v1.0/education/users")
|
||||
Expect(len(unsorted)).To(Equal(2))
|
||||
Expect(unsorted[0].GetId()).To(Equal("user1"))
|
||||
Expect(unsorted[1].GetId()).To(Equal("user2"))
|
||||
|
||||
byMail := getUsers("/graph/v1.0/education/users?$orderby=mail")
|
||||
Expect(len(byMail)).To(Equal(2))
|
||||
Expect(byMail[0].GetId()).To(Equal("user2"))
|
||||
Expect(byMail[1].GetId()).To(Equal("user1"))
|
||||
byMail = getUsers("/graph/v1.0/education/users?$orderby=mail%20asc")
|
||||
Expect(len(byMail)).To(Equal(2))
|
||||
Expect(byMail[0].GetId()).To(Equal("user2"))
|
||||
Expect(byMail[1].GetId()).To(Equal("user1"))
|
||||
byMail = getUsers("/graph/v1.0/education/users?$orderby=mail%20desc")
|
||||
Expect(len(byMail)).To(Equal(2))
|
||||
Expect(byMail[0].GetId()).To(Equal("user1"))
|
||||
Expect(byMail[1].GetId()).To(Equal("user2"))
|
||||
|
||||
byDisplayName := getUsers("/graph/v1.0/education/users?$orderby=displayName")
|
||||
Expect(len(byDisplayName)).To(Equal(2))
|
||||
Expect(byDisplayName[0].GetId()).To(Equal("user2"))
|
||||
Expect(byDisplayName[1].GetId()).To(Equal("user1"))
|
||||
byDisplayName = getUsers("/graph/v1.0/education/users?$orderby=displayName%20asc")
|
||||
Expect(len(byDisplayName)).To(Equal(2))
|
||||
Expect(byDisplayName[0].GetId()).To(Equal("user2"))
|
||||
Expect(byDisplayName[1].GetId()).To(Equal("user1"))
|
||||
byDisplayName = getUsers("/graph/v1.0/education/users?$orderby=displayName%20desc")
|
||||
Expect(len(byDisplayName)).To(Equal(2))
|
||||
Expect(byDisplayName[0].GetId()).To(Equal("user1"))
|
||||
Expect(byDisplayName[1].GetId()).To(Equal("user2"))
|
||||
|
||||
byOnPremisesSamAccountName := getUsers("/graph/v1.0/education/users?$orderby=onPremisesSamAccountName")
|
||||
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
|
||||
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
|
||||
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
|
||||
byOnPremisesSamAccountName = getUsers("/graph/v1.0/education/users?$orderby=onPremisesSamAccountName%20asc")
|
||||
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
|
||||
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
|
||||
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
|
||||
byOnPremisesSamAccountName = getUsers("/graph/v1.0/education/users?$orderby=onPremisesSamAccountName%20desc")
|
||||
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
|
||||
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user1"))
|
||||
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user2"))
|
||||
|
||||
// Handles invalid sort field
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users?$orderby=invalid", nil)
|
||||
svc.GetEducationUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetEducationUser", func() {
|
||||
It("handles missing userids", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users", nil)
|
||||
svc.GetEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("gets the user", func() {
|
||||
user := &libregraph.EducationUser{}
|
||||
user.SetId("user1")
|
||||
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", *user.Id)
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.GetEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
responseUser := &libregraph.EducationUser{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(len(responseUser.GetDrives())).To(Equal(0))
|
||||
})
|
||||
|
||||
It("includes the personal space if requested", func() {
|
||||
user := &libregraph.EducationUser{}
|
||||
user.SetId("user1")
|
||||
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
|
||||
gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
TotalBytes: 10,
|
||||
}, nil)
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*provider.StorageSpace{
|
||||
{
|
||||
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
|
||||
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
|
||||
SpaceType: "project",
|
||||
},
|
||||
{
|
||||
Id: &provider.StorageSpaceId{OpaqueId: "personal"},
|
||||
Root: &provider.ResourceId{SpaceId: "personal", OpaqueId: "personal"},
|
||||
SpaceType: "personal",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users?$expand=drive", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", *user.Id)
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.GetEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
responseUser := &libregraph.EducationUser{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(*responseUser.GetDrive().Id).To(Equal("personal"))
|
||||
})
|
||||
|
||||
It("includes the drives if requested", func() {
|
||||
user := &libregraph.EducationUser{}
|
||||
user.SetId("user1")
|
||||
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
|
||||
gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
TotalBytes: 10,
|
||||
}, nil)
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*provider.StorageSpace{
|
||||
{
|
||||
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
|
||||
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/users?$expand=drives", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", *user.Id)
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.GetEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
responseUser := &libregraph.EducationUser{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(len(responseUser.GetDrives())).To(Equal(1))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("PostEducationUser", func() {
|
||||
var (
|
||||
user *libregraph.EducationUser
|
||||
|
||||
assertHandleBadAttributes = func(user *libregraph.EducationUser) {
|
||||
userJson, err := json.Marshal(user)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users", bytes.NewBuffer(userJson))
|
||||
svc.PostEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
identity := libregraph.ObjectIdentity{}
|
||||
identity.SetIssuer("issu.er")
|
||||
identity.SetIssuerAssignedId("our-user.1")
|
||||
|
||||
user = &libregraph.EducationUser{}
|
||||
user.SetDisplayName("Display Name")
|
||||
user.SetOnPremisesSamAccountName("user")
|
||||
user.SetMail("user@example.com")
|
||||
user.SetAccountEnabled(true)
|
||||
user.SetIdentities([]libregraph.ObjectIdentity{identity})
|
||||
user.SetPrimaryRole("student")
|
||||
})
|
||||
|
||||
It("handles invalid bodies", func() {
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users?$invalid=true", nil)
|
||||
svc.PostEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles missing display names", func() {
|
||||
user.DisplayName = nil
|
||||
assertHandleBadAttributes(user)
|
||||
|
||||
})
|
||||
|
||||
It("handles missing OnPremisesSamAccountName", func() {
|
||||
user.OnPremisesSamAccountName = nil
|
||||
assertHandleBadAttributes(user)
|
||||
|
||||
user.SetOnPremisesSamAccountName("")
|
||||
assertHandleBadAttributes(user)
|
||||
})
|
||||
|
||||
It("handles bad Mails", func() {
|
||||
user.Mail = nil
|
||||
assertHandleBadAttributes(user)
|
||||
|
||||
user.SetMail("not-a-mail-address")
|
||||
assertHandleBadAttributes(user)
|
||||
})
|
||||
|
||||
It("handles set Ids - they are read-only", func() {
|
||||
user.SetId("/users/user")
|
||||
assertHandleBadAttributes(user)
|
||||
})
|
||||
|
||||
It("creates a user", func() {
|
||||
roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settingssvc.AssignRoleToUserResponse{}, nil)
|
||||
identityEducationBackend.On("CreateEducationUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.EducationUser) *libregraph.EducationUser {
|
||||
user.SetId("/users/user")
|
||||
return &user
|
||||
}, nil)
|
||||
userJson, err := json.Marshal(user)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users", bytes.NewBuffer(userJson))
|
||||
r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
|
||||
svc.PostEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("DeleteEducationUser", func() {
|
||||
It("handles missing userids", func() {
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/users/{userid}", nil)
|
||||
svc.DeleteEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("prevents a user from deleting themselves", func() {
|
||||
lu := libregraph.EducationUser{}
|
||||
lu.SetId(currentUser.Id.OpaqueId)
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/users/{userid}", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", currentUser.Id.OpaqueId)
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusForbidden))
|
||||
})
|
||||
|
||||
It("deletes a user from deleting themselves", func() {
|
||||
otheruser := &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "otheruser",
|
||||
},
|
||||
}
|
||||
|
||||
lu := libregraph.EducationUser{}
|
||||
lu.SetId(otheruser.Id.OpaqueId)
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
|
||||
identityEducationBackend.On("DeleteEducationUser", mock.Anything, mock.Anything).Return(nil)
|
||||
gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
}, nil)
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*provider.StorageSpace{
|
||||
{
|
||||
Opaque: &typesv1beta1.Opaque{},
|
||||
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
|
||||
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
|
||||
SpaceType: "personal",
|
||||
Owner: otheruser,
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/users/{userid}", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", lu.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
gatewayClient.AssertNumberOfCalls(GinkgoT(), "DeleteStorageSpace", 2) // 2 calls for the home space. first trash, then purge
|
||||
})
|
||||
})
|
||||
|
||||
Describe("PatchEducationUser", func() {
|
||||
var (
|
||||
user *libregraph.EducationUser
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
user = &libregraph.EducationUser{}
|
||||
user.SetDisplayName("Display Name")
|
||||
user.SetOnPremisesSamAccountName("user")
|
||||
user.SetMail("user@example.com")
|
||||
user.SetId("/users/user")
|
||||
|
||||
identityEducationBackend.On("GetEducationUser", mock.Anything, mock.Anything, mock.Anything).Return(&user, nil)
|
||||
})
|
||||
|
||||
It("handles missing userids", func() {
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/users/{userid}", nil)
|
||||
svc.PatchEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles invalid bodies", func() {
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users?$invalid=true", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles invalid email", func() {
|
||||
user.SetMail("invalid")
|
||||
data, err := json.Marshal(user)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users?$invalid=true", bytes.NewBuffer(data))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("updates attributes", func() {
|
||||
identityEducationBackend.On("UpdateEducationUser", mock.Anything, user.GetId(), mock.Anything).Return(user, nil)
|
||||
|
||||
user.SetDisplayName("New Display Name")
|
||||
data, err := json.Marshal(user)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/users?$invalid=true", bytes.NewBuffer(data))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchEducationUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err = io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
updatedUser := libregraph.EducationUser{}
|
||||
err = json.Unmarshal(data, &updatedUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(updatedUser.GetDisplayName()).To(Equal("New Display Name"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -246,6 +246,7 @@ func (g Graph) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
// GetGroupMembers implements the Service interface.
|
||||
func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling get group members")
|
||||
@@ -408,7 +409,7 @@ func sortGroups(req *godata.GoDataRequest, groups []*libregraph.Group) ([]*libre
|
||||
return groups, nil
|
||||
}
|
||||
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
|
||||
case "displayName":
|
||||
case displayNameAttr:
|
||||
sorter = groupsByDisplayName{groups}
|
||||
default:
|
||||
return nil, fmt.Errorf("we do not support <%s> as a order parameter", req.Query.OrderBy.OrderByItems[0].Field.Value)
|
||||
|
||||
@@ -139,6 +139,31 @@ func (i instrument) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUsers implements the Service interface.
|
||||
func (i instrument) GetEducationUsers(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetEducationUsers(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUser implements the Service interface.
|
||||
func (i instrument) GetEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PostEducationUser implements the Service interface.
|
||||
func (i instrument) PostEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PostEducationUser(w, r)
|
||||
}
|
||||
|
||||
// DeleteEducationUser implements the Service interface.
|
||||
func (i instrument) DeleteEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PatchEducationUser implements the Service interface.
|
||||
func (i instrument) PatchEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PatchEducationUser(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (i instrument) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetDrives(w, r)
|
||||
|
||||
@@ -139,6 +139,31 @@ func (l logging) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUsers implements the Service interface.
|
||||
func (l logging) GetEducationUsers(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetEducationUsers(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUser implements the Service interface.
|
||||
func (l logging) GetEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PostEducationUser implements the Service interface.
|
||||
func (l logging) PostEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PostEducationUser(w, r)
|
||||
}
|
||||
|
||||
// DeleteEducationUser implements the Service interface.
|
||||
func (l logging) DeleteEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PatchEducationUser implements the Service interface.
|
||||
func (l logging) PatchEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PatchEducationUser(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (l logging) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetDrives(w, r)
|
||||
|
||||
@@ -119,3 +119,41 @@ type schoolsByDisplayName struct {
|
||||
func (g schoolsByDisplayName) Less(i, j int) bool {
|
||||
return strings.ToLower(g.schoolSlice[i].GetDisplayName()) < strings.ToLower(g.schoolSlice[j].GetDisplayName())
|
||||
}
|
||||
|
||||
type educationUserSlice []*libregraph.EducationUser
|
||||
|
||||
// Len is the number of elements in the collection.
|
||||
func (d educationUserSlice) Len() int { return len(d) }
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (d educationUserSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
|
||||
type educationUsersByDisplayName struct {
|
||||
educationUserSlice
|
||||
}
|
||||
|
||||
type educationUsersByMail struct {
|
||||
educationUserSlice
|
||||
}
|
||||
|
||||
type educationUsersByOnPremisesSamAccountName struct {
|
||||
educationUserSlice
|
||||
}
|
||||
|
||||
// Less reports whether the element with index i
|
||||
// must sort before the element with index j.
|
||||
func (u educationUsersByDisplayName) Less(i, j int) bool {
|
||||
return strings.ToLower(u.educationUserSlice[i].GetDisplayName()) < strings.ToLower(u.educationUserSlice[j].GetDisplayName())
|
||||
}
|
||||
|
||||
// Less reports whether the element with index i
|
||||
// must sort before the element with index j.
|
||||
func (u educationUsersByMail) Less(i, j int) bool {
|
||||
return strings.ToLower(u.educationUserSlice[i].GetMail()) < strings.ToLower(u.educationUserSlice[j].GetMail())
|
||||
}
|
||||
|
||||
// Less reports whether the element with index i
|
||||
// must sort before the element with index j.
|
||||
func (u educationUsersByOnPremisesSamAccountName) Less(i, j int) bool {
|
||||
return strings.ToLower(u.educationUserSlice[i].GetOnPremisesSamAccountName()) < strings.ToLower(u.educationUserSlice[j].GetOnPremisesSamAccountName())
|
||||
}
|
||||
|
||||
@@ -63,12 +63,6 @@ func (g Graph) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := school.GetDisplayNameOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("school", school).Msg("could not create school: missing required attribute")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Missing Required Attribute")
|
||||
return
|
||||
}
|
||||
|
||||
// Disallow user-supplied IDs. It's supposed to be readonly. We're either
|
||||
// generating them in the backend ourselves or rely on the Backend's
|
||||
// storage (e.g. LDAP) to provide a unique ID.
|
||||
@@ -78,6 +72,18 @@ func (g Graph) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := school.GetDisplayNameOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("school", school).Msg("could not create school: missing required attribute")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Missing Required Attribute")
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := school.GetSchoolNumberOk(); !ok {
|
||||
logger.Debug().Err(err).Interface("school", school).Msg("could not create school: missing required attribute")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Missing Required Attribute")
|
||||
return
|
||||
}
|
||||
|
||||
if school, err = g.identityEducationBackend.CreateSchool(r.Context(), *school); err != nil {
|
||||
logger.Debug().Interface("school", school).Msg("could not create school: backend error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
@@ -391,7 +397,7 @@ func sortSchools(req *godata.GoDataRequest, schools []*libregraph.EducationSchoo
|
||||
return schools, nil
|
||||
}
|
||||
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
|
||||
case "displayName":
|
||||
case displayNameAttr:
|
||||
sorter = schoolsByDisplayName{schools}
|
||||
default:
|
||||
return nil, fmt.Errorf("we do not support <%s> as a order parameter", req.Query.OrderBy.OrderByItems[0].Field.Value)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
@@ -37,7 +37,6 @@ var _ = Describe("Schools", func() {
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
gatewayClient *mocks.GatewayClient
|
||||
eventsPublisher mocks.Publisher
|
||||
identityEducationBackend *identitymocks.EducationBackend
|
||||
|
||||
rr *httptest.ResponseRecorder
|
||||
@@ -51,7 +50,6 @@ var _ = Describe("Schools", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
identityEducationBackend = &identitymocks.EducationBackend{}
|
||||
gatewayClient = &mocks.GatewayClient{}
|
||||
@@ -71,7 +69,6 @@ var _ = Describe("Schools", func() {
|
||||
svc = service.NewService(
|
||||
service.Config(cfg),
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
service.WithIdentityEducationBackend(identityEducationBackend),
|
||||
)
|
||||
})
|
||||
@@ -91,7 +88,7 @@ var _ = Describe("Schools", func() {
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
@@ -106,7 +103,7 @@ var _ = Describe("Schools", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools", nil)
|
||||
svc.GetSchools(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusInternalServerError))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
@@ -122,7 +119,7 @@ var _ = Describe("Schools", func() {
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusInternalServerError))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
@@ -138,7 +135,7 @@ var _ = Describe("Schools", func() {
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := service.ListResponse{}
|
||||
@@ -154,7 +151,7 @@ var _ = Describe("Schools", func() {
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := schoolList{}
|
||||
@@ -265,6 +262,7 @@ var _ = Describe("Schools", func() {
|
||||
It("creates the school", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetDisplayName("New School")
|
||||
newSchool.SetSchoolNumber("0034")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -346,15 +344,14 @@ var _ = Describe("Schools", func() {
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "DeleteSchool", 1)
|
||||
eventsPublisher.AssertNumberOfCalls(GinkgoT(), "Publish", 1)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetSchoolMembers", func() {
|
||||
It("gets the list of members", func() {
|
||||
user := libregraph.NewUser()
|
||||
user := libregraph.NewEducationUser()
|
||||
user.SetId("user")
|
||||
identityEducationBackend.On("GetSchoolMembers", mock.Anything, mock.Anything, mock.Anything).Return([]*libregraph.User{user}, nil)
|
||||
identityEducationBackend.On("GetSchoolMembers", mock.Anything, mock.Anything, mock.Anything).Return([]*libregraph.EducationUser{user}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools/{schoolID}/members", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
@@ -363,7 +360,7 @@ var _ = Describe("Schools", func() {
|
||||
svc.GetSchoolMembers(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var members []*libregraph.User
|
||||
|
||||
@@ -23,7 +23,8 @@ import (
|
||||
|
||||
const (
|
||||
// HeaderPurge defines the header name for the purge header.
|
||||
HeaderPurge = "Purge"
|
||||
HeaderPurge = "Purge"
|
||||
displayNameAttr = "displayName"
|
||||
)
|
||||
|
||||
// Service defines the service handlers.
|
||||
@@ -55,6 +56,12 @@ type Service interface {
|
||||
PostSchoolMember(http.ResponseWriter, *http.Request)
|
||||
DeleteSchoolMember(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetEducationUsers(http.ResponseWriter, *http.Request)
|
||||
GetEducationUser(http.ResponseWriter, *http.Request)
|
||||
PostEducationUser(http.ResponseWriter, *http.Request)
|
||||
DeleteEducationUser(http.ResponseWriter, *http.Request)
|
||||
PatchEducationUser(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetDrives(w http.ResponseWriter, r *http.Request)
|
||||
GetSingleDrive(w http.ResponseWriter, r *http.Request)
|
||||
GetAllDrives(w http.ResponseWriter, r *http.Request)
|
||||
@@ -251,12 +258,12 @@ func NewService(opts ...Option) Service {
|
||||
})
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUsers)
|
||||
r.Post("/", svc.PostUser)
|
||||
r.Get("/", svc.GetEducationUsers)
|
||||
r.Post("/", svc.PostEducationUser)
|
||||
r.Route("/{userID}", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUser)
|
||||
r.Delete("/", svc.DeleteUser)
|
||||
r.Patch("/", svc.PatchUser)
|
||||
r.Get("/", svc.GetEducationUser)
|
||||
r.Delete("/", svc.DeleteEducationUser)
|
||||
r.Patch("/", svc.PatchEducationUser)
|
||||
})
|
||||
})
|
||||
r.Route("/classes", func(r chi.Router) {
|
||||
|
||||
@@ -135,6 +135,31 @@ func (t tracing) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUsers implements the Service interface.
|
||||
func (t tracing) GetEducationUsers(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetEducationUsers(w, r)
|
||||
}
|
||||
|
||||
// GetEducationUser implements the Service interface.
|
||||
func (t tracing) GetEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PostEducationUser implements the Service interface.
|
||||
func (t tracing) PostEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PostEducationUser(w, r)
|
||||
}
|
||||
|
||||
// DeleteEducationUser implements the Service interface.
|
||||
func (t tracing) DeleteEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteEducationUser(w, r)
|
||||
}
|
||||
|
||||
// PatchEducationUser implements the Service interface.
|
||||
func (t tracing) PatchEducationUser(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PatchEducationUser(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (t tracing) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetDrives(w, r)
|
||||
|
||||
@@ -97,6 +97,7 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, &ListResponse{Value: users})
|
||||
}
|
||||
|
||||
// PostUser implements the Service interface.
|
||||
func (g Graph) PostUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("body", r.Body).Msg("calling create user")
|
||||
@@ -278,6 +279,7 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, user)
|
||||
}
|
||||
|
||||
// DeleteUser implements the Service interface.
|
||||
func (g Graph) DeleteUser(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling delete user")
|
||||
@@ -489,7 +491,7 @@ func sortUsers(req *godata.GoDataRequest, users []*libregraph.User) ([]*libregra
|
||||
return users, nil
|
||||
}
|
||||
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
|
||||
case "displayName":
|
||||
case displayNameAttr:
|
||||
sorter = usersByDisplayName{users}
|
||||
case "mail":
|
||||
sorter = usersByMail{users}
|
||||
|
||||
Reference in New Issue
Block a user