mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-24 08:27:27 -04:00
initial schools API
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
committed by
Ralf Haferkamp
parent
26e494ec8b
commit
9844f5f8ce
@@ -31,6 +31,7 @@ ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this
|
||||
$(MOCKERY) --dir pkg/service/v0 --case underscore --name Permissions
|
||||
$(MOCKERY) --dir pkg/service/v0 --case underscore --name RoleService
|
||||
$(MOCKERY) --dir pkg/identity --output pkg/identity/mocks --case underscore --name Backend
|
||||
$(MOCKERY) --dir pkg/identity --output pkg/identity/mocks --case underscore --name EducationBackend
|
||||
$(MOCKERY) --srcpkg github.com/go-ldap/ldap/v3 --case underscore --filename ldapclient.go --name Client
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
)
|
||||
|
||||
// Backend defines the Interface for an IdentityBackend implementation
|
||||
type Backend interface {
|
||||
// CreateUser creates a given user in the identity backend.
|
||||
CreateUser(ctx context.Context, user libregraph.User) (*libregraph.User, error)
|
||||
@@ -31,6 +32,23 @@ type Backend interface {
|
||||
RemoveMemberFromGroup(ctx context.Context, groupID string, memberID string) error
|
||||
}
|
||||
|
||||
// EducationBackend defines the Interface for an EducationBackend implementation
|
||||
type EducationBackend interface {
|
||||
// CreateSchool creates the supplied school in the identity backend.
|
||||
CreateSchool(ctx context.Context, group libregraph.EducationSchool) (*libregraph.EducationSchool, error)
|
||||
// DeleteSchool deletes a given school, identified by id
|
||||
DeleteSchool(ctx context.Context, id string) error
|
||||
// GetSchool reads a given school by id
|
||||
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)
|
||||
// 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
|
||||
}
|
||||
|
||||
func CreateUserModelFromCS3(u *cs3.User) *libregraph.User {
|
||||
if u.Id == nil {
|
||||
u.Id = &cs3.UserId{}
|
||||
|
||||
65
services/graph/pkg/identity/ldap_school.go
Normal file
65
services/graph/pkg/identity/ldap_school.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
)
|
||||
|
||||
type schoolConfig struct {
|
||||
schoolBaseDN string
|
||||
schoolFilter string
|
||||
schoolObjectClass string
|
||||
schoolScope int
|
||||
schoolAttributeMap schoolAttributeMap
|
||||
}
|
||||
|
||||
type schoolAttributeMap struct {
|
||||
displayname string
|
||||
schoolNumber string
|
||||
id string
|
||||
}
|
||||
|
||||
func newSchoolAttributeMap() schoolAttributeMap {
|
||||
return schoolAttributeMap{
|
||||
displayname: "ou",
|
||||
schoolNumber: "ocEducationSchoolNumber",
|
||||
id: "owncloudUUID",
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSchool creates the supplied school in the identity backend.
|
||||
func (i *LDAP) CreateSchool(ctx context.Context, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// DeleteSchool deletes a given school, identified by id
|
||||
func (i *LDAP) DeleteSchool(ctx context.Context, id string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// GetSchool implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetSchool(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.EducationSchool, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetSchools implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetSchools(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationSchool, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.User, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
|
||||
// AddMembersToSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
||||
func (i *LDAP) AddMembersToSchool(ctx context.Context, schoolID string, memberID []string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
|
||||
// RemoveMemberFromSchool removes a single member (by ID) from a school
|
||||
func (i *LDAP) RemoveMemberFromSchool(ctx context.Context, schoolID string, memberID string) error {
|
||||
return errNotImplemented
|
||||
}
|
||||
167
services/graph/pkg/identity/mocks/education_backend.go
Normal file
167
services/graph/pkg/identity/mocks/education_backend.go
Normal file
@@ -0,0 +1,167 @@
|
||||
// Code generated by mockery v2.14.1. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
url "net/url"
|
||||
)
|
||||
|
||||
// EducationBackend is an autogenerated mock type for the EducationBackend type
|
||||
type EducationBackend struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// AddMembersToSchool provides a mock function with given fields: ctx, schoolID, memberID
|
||||
func (_m *EducationBackend) AddMembersToSchool(ctx context.Context, schoolID string, memberID []string) error {
|
||||
ret := _m.Called(ctx, schoolID, memberID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, []string) error); ok {
|
||||
r0 = rf(ctx, schoolID, memberID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
var r0 *libregraph.EducationSchool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, libregraph.EducationSchool) *libregraph.EducationSchool); ok {
|
||||
r0 = rf(ctx, group)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*libregraph.EducationSchool)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, libregraph.EducationSchool) error); ok {
|
||||
r1 = rf(ctx, group)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
var r0 *libregraph.EducationSchool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, url.Values) *libregraph.EducationSchool); ok {
|
||||
r0 = rf(ctx, nameOrID, queryParam)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*libregraph.EducationSchool)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// GetSchoolMembers provides a mock function with given fields: ctx, id
|
||||
func (_m *EducationBackend) GetSchoolMembers(ctx context.Context, id string) ([]*libregraph.User, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
|
||||
var r0 []*libregraph.User
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.User); ok {
|
||||
r0 = rf(ctx, id)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*libregraph.User)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||
r1 = rf(ctx, id)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetSchools provides a mock function with given fields: ctx, queryParam
|
||||
func (_m *EducationBackend) GetSchools(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationSchool, error) {
|
||||
ret := _m.Called(ctx, queryParam)
|
||||
|
||||
var r0 []*libregraph.EducationSchool
|
||||
if rf, ok := ret.Get(0).(func(context.Context, url.Values) []*libregraph.EducationSchool); ok {
|
||||
r0 = rf(ctx, queryParam)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*libregraph.EducationSchool)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// RemoveMemberFromSchool provides a mock function with given fields: ctx, schoolID, memberID
|
||||
func (_m *EducationBackend) RemoveMemberFromSchool(ctx context.Context, schoolID string, memberID string) error {
|
||||
ret := _m.Called(ctx, schoolID, memberID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
|
||||
r0 = rf(ctx, schoolID, memberID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewEducationBackend interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewEducationBackend creates a new instance of EducationBackend. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewEducationBackend(t mockConstructorTestingTNewEducationBackend) *EducationBackend {
|
||||
mock := &EducationBackend{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package svc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
@@ -90,16 +92,17 @@ type RoleService interface {
|
||||
|
||||
// Graph defines implements the business logic for Service.
|
||||
type Graph struct {
|
||||
config *config.Config
|
||||
mux *chi.Mux
|
||||
logger *log.Logger
|
||||
identityBackend identity.Backend
|
||||
gatewayClient GatewayClient
|
||||
roleService RoleService
|
||||
permissionsService Permissions
|
||||
spacePropertiesCache *ttlcache.Cache
|
||||
eventsPublisher events.Publisher
|
||||
searchService searchsvc.SearchProviderService
|
||||
config *config.Config
|
||||
mux *chi.Mux
|
||||
logger *log.Logger
|
||||
identityBackend identity.Backend
|
||||
identityEducationBackend identity.EducationBackend
|
||||
gatewayClient GatewayClient
|
||||
roleService RoleService
|
||||
permissionsService Permissions
|
||||
spacePropertiesCache *ttlcache.Cache
|
||||
eventsPublisher events.Publisher
|
||||
searchService searchsvc.SearchProviderService
|
||||
}
|
||||
|
||||
// ServeHTTP implements the Service interface.
|
||||
@@ -107,7 +110,7 @@ func (g Graph) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
g.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// GetClient returns a gateway client to talk to reva
|
||||
// GetGatewayClient returns a gateway client to talk to reva
|
||||
func (g Graph) GetGatewayClient() GatewayClient {
|
||||
return g.gatewayClient
|
||||
}
|
||||
@@ -131,6 +134,7 @@ func (g Graph) getWebDavBaseURL() (*url.URL, error) {
|
||||
return webDavBaseURL, nil
|
||||
}
|
||||
|
||||
// ListResponse is used for proper marshalling of Graph list responses
|
||||
type ListResponse struct {
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
@@ -139,3 +143,18 @@ const (
|
||||
ReadmeSpecialFolderName = "readme"
|
||||
SpaceImageSpecialFolderName = "image"
|
||||
)
|
||||
|
||||
// TODO might be different for /education/users vs /users
|
||||
func (g Graph) parseMemberRef(ref string) (string, string, error) {
|
||||
memberURL, err := url.ParseRequestURI(ref)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
segments := strings.Split(memberURL.Path, "/")
|
||||
if len(segments) < 2 {
|
||||
return "", "", errors.New("invalid member reference")
|
||||
}
|
||||
id := segments[len(segments)-1]
|
||||
memberType := segments[len(segments)-2]
|
||||
return memberType, id, nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
const memberRefsLimit = 20
|
||||
const memberTypeUsers = "users"
|
||||
|
||||
// GetGroups implements the Service interface.
|
||||
func (g Graph) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -145,7 +146,7 @@ func (g Graph) PatchGroup(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Debug().Str("membertype", memberType).Str("memberid", id).Msg("add group member")
|
||||
// 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" {
|
||||
if memberType != memberTypeUsers {
|
||||
logger.Debug().
|
||||
Str("type", memberType).
|
||||
Msg("could not change group: could not add member, only user type is allowed")
|
||||
@@ -324,7 +325,7 @@ func (g Graph) PostGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// 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" {
|
||||
if memberType != memberTypeUsers {
|
||||
logger.Debug().Str("type", memberType).Msg("could not add group member: Only users are allowed as group members")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Only users are allowed as group members")
|
||||
return
|
||||
@@ -401,20 +402,6 @@ func (g Graph) DeleteGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func (g Graph) parseMemberRef(ref string) (string, string, error) {
|
||||
memberURL, err := url.ParseRequestURI(ref)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
segments := strings.Split(memberURL.Path, "/")
|
||||
if len(segments) < 2 {
|
||||
return "", "", errors.New("invalid member reference")
|
||||
}
|
||||
id := segments[len(segments)-1]
|
||||
memberType := segments[len(segments)-2]
|
||||
return memberType, id, nil
|
||||
}
|
||||
|
||||
func sortGroups(req *godata.GoDataRequest, groups []*libregraph.Group) ([]*libregraph.Group, error) {
|
||||
var sorter sort.Interface
|
||||
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
|
||||
|
||||
@@ -99,6 +99,46 @@ func (i instrument) DeleteGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteGroupMember(w, r)
|
||||
}
|
||||
|
||||
// GetSchools implements the Service interface.
|
||||
func (i instrument) GetSchools(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetSchools(w, r)
|
||||
}
|
||||
|
||||
// GetSchool implements the Service interface.
|
||||
func (i instrument) GetSchool(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetSchool(w, r)
|
||||
}
|
||||
|
||||
// PostSchool implements the Service interface.
|
||||
func (i instrument) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PostSchool(w, r)
|
||||
}
|
||||
|
||||
// PatchSchool implements the Service interface.
|
||||
func (i instrument) PatchSchool(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PatchSchool(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchool implements the Service interface.
|
||||
func (i instrument) DeleteSchool(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteSchool(w, r)
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the Service interface.
|
||||
func (i instrument) GetSchoolMembers(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetSchoolMembers(w, r)
|
||||
}
|
||||
|
||||
// PostSchoolMember implements the Service interface.
|
||||
func (i instrument) PostSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.PostSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchoolMember implements the Service interface.
|
||||
func (i instrument) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (i instrument) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetDrives(w, r)
|
||||
|
||||
@@ -99,6 +99,46 @@ func (l logging) DeleteGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteGroupMember(w, r)
|
||||
}
|
||||
|
||||
// GetSchools implements the Service interface.
|
||||
func (l logging) GetSchools(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetSchools(w, r)
|
||||
}
|
||||
|
||||
// GetSchool implements the Service interface.
|
||||
func (l logging) GetSchool(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetSchool(w, r)
|
||||
}
|
||||
|
||||
// PostSchool implements the Service interface.
|
||||
func (l logging) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PostSchool(w, r)
|
||||
}
|
||||
|
||||
// PatchSchool implements the Service interface.
|
||||
func (l logging) PatchSchool(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PatchSchool(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchool implements the Service interface.
|
||||
func (l logging) DeleteSchool(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteSchool(w, r)
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the Service interface.
|
||||
func (l logging) GetSchoolMembers(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetSchoolMembers(w, r)
|
||||
}
|
||||
|
||||
// PostSchoolMember implements the Service interface.
|
||||
func (l logging) PostSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.PostSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchoolMember implements the Service interface.
|
||||
func (l logging) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// GetDrives implements the Service interface.
|
||||
func (l logging) GetDrives(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetDrives(w, r)
|
||||
|
||||
@@ -17,17 +17,18 @@ type Option func(o *Options)
|
||||
|
||||
// Options defines the available options for this package.
|
||||
type Options struct {
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
RequireAdminMiddleware func(http.Handler) http.Handler
|
||||
GatewayClient GatewayClient
|
||||
IdentityBackend identity.Backend
|
||||
RoleService RoleService
|
||||
PermissionService Permissions
|
||||
RoleManager *roles.Manager
|
||||
EventsPublisher events.Publisher
|
||||
SearchService searchsvc.SearchProviderService
|
||||
Logger log.Logger
|
||||
Config *config.Config
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
RequireAdminMiddleware func(http.Handler) http.Handler
|
||||
GatewayClient GatewayClient
|
||||
IdentityBackend identity.Backend
|
||||
IdentityEducationBackend identity.EducationBackend
|
||||
RoleService RoleService
|
||||
PermissionService Permissions
|
||||
RoleManager *roles.Manager
|
||||
EventsPublisher events.Publisher
|
||||
SearchService searchsvc.SearchProviderService
|
||||
}
|
||||
|
||||
// newOptions initializes the available default options.
|
||||
@@ -83,6 +84,13 @@ func WithIdentityBackend(val identity.Backend) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithIdentityEducationBackend provides a function to set the IdentityEducationBackend option.
|
||||
func WithIdentityEducationBackend(val identity.EducationBackend) Option {
|
||||
return func(o *Options) {
|
||||
o.IdentityEducationBackend = val
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoleService provides a function to set the RoleService option.
|
||||
func WithRoleService(val RoleService) Option {
|
||||
return func(o *Options) {
|
||||
|
||||
@@ -101,3 +101,21 @@ type groupsByDisplayName struct {
|
||||
func (g groupsByDisplayName) Less(i, j int) bool {
|
||||
return strings.ToLower(g.groupSlice[i].GetDisplayName()) < strings.ToLower(g.groupSlice[j].GetDisplayName())
|
||||
}
|
||||
|
||||
type schoolSlice []*libregraph.EducationSchool
|
||||
|
||||
// Len is the number of elements in the collection.
|
||||
func (d schoolSlice) Len() int { return len(d) }
|
||||
|
||||
// Swap swaps the elements with indexes i and j.
|
||||
func (d schoolSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
|
||||
|
||||
type schoolsByDisplayName struct {
|
||||
schoolSlice
|
||||
}
|
||||
|
||||
// Less reports whether the element with index i
|
||||
// must sort before the element with index j.
|
||||
func (g schoolsByDisplayName) Less(i, j int) bool {
|
||||
return strings.ToLower(g.schoolSlice[i].GetDisplayName()) < strings.ToLower(g.schoolSlice[j].GetDisplayName())
|
||||
}
|
||||
|
||||
397
services/graph/pkg/service/v0/schools.go
Normal file
397
services/graph/pkg/service/v0/schools.go
Normal file
@@ -0,0 +1,397 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
|
||||
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/cs3org/reva/v2/pkg/events"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
// GetSchools implements the Service interface.
|
||||
func (g Graph) GetSchools(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling get schools")
|
||||
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 schools: query error")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
schools, err := g.identityEducationBackend.GetSchools(r.Context(), r.URL.Query())
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get schools: 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
|
||||
}
|
||||
|
||||
schools, err = sortSchools(odataReq, schools)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("cannot get schools: could not sort schools according to query")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: schools})
|
||||
}
|
||||
|
||||
// PostSchool implements the Service interface.
|
||||
func (g Graph) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling post school")
|
||||
school := libregraph.NewEducationSchool()
|
||||
err := json.NewDecoder(r.Body).Decode(school)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not create school: invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %s", err.Error()))
|
||||
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.
|
||||
if _, ok := school.GetIdOk(); ok {
|
||||
logger.Debug().Msg("could not create school: id is a read-only attribute")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "school id is a read-only 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())
|
||||
return
|
||||
}
|
||||
|
||||
if school != nil && school.Id != nil {
|
||||
e := events.SchoolCreated{SchoolID: *school.Id}
|
||||
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, school)
|
||||
}
|
||||
|
||||
// PatchSchool implements the Service interface.
|
||||
func (g Graph) PatchSchool(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling patch school")
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Str("id", schoolID).Msg("could not change school: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not change school: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
changes := libregraph.NewEducationSchool()
|
||||
err = json.NewDecoder(r.Body).Decode(changes)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not change school: invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
e := events.SchoolFeatureChanged{SchoolID: schoolID}
|
||||
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
// GetSchool implements the Service interface.
|
||||
func (g Graph) GetSchool(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling get school")
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Str("id", schoolID).Msg("could not get school: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not get school: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().
|
||||
Str("id", schoolID).
|
||||
Interface("query", r.URL.Query()).
|
||||
Msg("calling get school on backend")
|
||||
school, err := g.identityEducationBackend.GetSchool(r.Context(), schoolID, r.URL.Query())
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get school: 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())
|
||||
}
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, school)
|
||||
}
|
||||
|
||||
// DeleteSchool implements the Service interface.
|
||||
func (g Graph) DeleteSchool(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling delete school")
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", schoolID).Msg("could not delete school: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not delete school: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("id", schoolID).Msg("calling delete school on backend")
|
||||
err = g.identityEducationBackend.DeleteSchool(r.Context(), schoolID)
|
||||
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not delete school: 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.SchoolDeleted{SchoolID: schoolID}
|
||||
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the Service interface.
|
||||
func (g Graph) GetSchoolMembers(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling get school members")
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Str("id", schoolID).Msg("could not get school members: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not get school members: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("id", schoolID).Msg("calling get school members on backend")
|
||||
members, err := g.identityEducationBackend.GetSchoolMembers(r.Context(), schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get school members: 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
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, members)
|
||||
}
|
||||
|
||||
// PostSchoolMember implements the Service interface.
|
||||
func (g Graph) PostSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("Calling post school member")
|
||||
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().
|
||||
Err(err).
|
||||
Str("id", schoolID).
|
||||
Msg("could not add member to school: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not add school member: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
memberRef := libregraph.NewMemberReference()
|
||||
err = json.NewDecoder(r.Body).Decode(memberRef)
|
||||
if err != nil {
|
||||
logger.Debug().
|
||||
Err(err).
|
||||
Interface("body", r.Body).
|
||||
Msg("could not add school member: invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
memberRefURL, ok := memberRef.GetOdataIdOk()
|
||||
if !ok {
|
||||
logger.Debug().Msg("could not add school member: @odata.id reference is missing")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "@odata.id reference is missing")
|
||||
return
|
||||
}
|
||||
memberType, id, err := g.parseMemberRef(*memberRefURL)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not add school member: error parsing @odata.id url")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Error parsing @odata.id url")
|
||||
return
|
||||
}
|
||||
// The MS Graph spec allows "directoryObject", "user", "school" and "organizational Contact"
|
||||
// we restrict this to users for now. Might add Schools as members later
|
||||
if memberType != "users" {
|
||||
logger.Debug().Str("type", memberType).Msg("could not add school member: Only users are allowed as school members")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Only users are allowed as school members")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug().Str("memberType", memberType).Str("id", id).Msg("calling add member on backend")
|
||||
err = g.identityEducationBackend.AddMembersToSchool(r.Context(), schoolID, []string{id})
|
||||
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not add school member: 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.SchoolMemberAdded{SchoolID: schoolID, UserID: id}
|
||||
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchoolMember implements the Service interface.
|
||||
func (g Graph) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling delete school member")
|
||||
|
||||
schoolID := chi.URLParam(r, "schoolID")
|
||||
schoolID, err := url.PathUnescape(schoolID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", schoolID).Msg("could not delete school member: unescaping school id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if schoolID == "" {
|
||||
logger.Debug().Msg("could not delete school member: missing school id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||
return
|
||||
}
|
||||
|
||||
memberID := chi.URLParam(r, "memberID")
|
||||
memberID, err = url.PathUnescape(memberID)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("id", memberID).Msg("could not delete school member: unescaping member id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping member id failed")
|
||||
return
|
||||
}
|
||||
|
||||
if memberID == "" {
|
||||
logger.Debug().Msg("could not delete school member: missing member id")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing member id")
|
||||
return
|
||||
}
|
||||
logger.Debug().Str("schoolID", schoolID).Str("memberID", memberID).Msg("calling delete member on backend")
|
||||
err = g.identityEducationBackend.RemoveMemberFromSchool(r.Context(), schoolID, memberID)
|
||||
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not delete school member: 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.SchoolMemberRemoved{SchoolID: schoolID, UserID: memberID}
|
||||
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||
e.Executant = currentUser.GetId()
|
||||
}
|
||||
g.publishEvent(e)
|
||||
|
||||
render.Status(r, http.StatusNoContent)
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func sortSchools(req *godata.GoDataRequest, schools []*libregraph.EducationSchool) ([]*libregraph.EducationSchool, error) {
|
||||
var sorter sort.Interface
|
||||
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
|
||||
return schools, nil
|
||||
}
|
||||
switch req.Query.OrderBy.OrderByItems[0].Field.Value {
|
||||
case "displayName":
|
||||
sorter = schoolsByDisplayName{schools}
|
||||
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 schools, nil
|
||||
}
|
||||
465
services/graph/pkg/service/v0/schools_test.go
Normal file
465
services/graph/pkg/service/v0/schools_test.go
Normal file
@@ -0,0 +1,465 @@
|
||||
package svc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/go-chi/chi/v5"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/test-go/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"
|
||||
"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"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
type schoolList struct {
|
||||
Value []*libregraph.EducationSchool
|
||||
}
|
||||
|
||||
var _ = Describe("Schools", func() {
|
||||
var (
|
||||
svc service.Service
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
gatewayClient *mocks.GatewayClient
|
||||
eventsPublisher mocks.Publisher
|
||||
identityEducationBackend *identitymocks.EducationBackend
|
||||
|
||||
rr *httptest.ResponseRecorder
|
||||
|
||||
newSchool *libregraph.EducationSchool
|
||||
currentUser = &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "user",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
identityEducationBackend = &identitymocks.EducationBackend{}
|
||||
gatewayClient = &mocks.GatewayClient{}
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetId("school1")
|
||||
|
||||
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),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("GetSchools", func() {
|
||||
It("handles invalid ODATA parameters", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools?§foo=bar", nil)
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles invalid sorting queries", func() {
|
||||
identityEducationBackend.On("GetSchools", ctx, mock.Anything).Return([]*libregraph.EducationSchool{newSchool}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools?$orderby=invalid", nil)
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
err = json.Unmarshal(data, &odataerr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(odataerr.Error.Code).To(Equal("invalidRequest"))
|
||||
})
|
||||
|
||||
It("handles unknown backend errors", func() {
|
||||
identityEducationBackend.On("GetSchools", ctx, mock.Anything).Return(nil, errors.New("failed"))
|
||||
|
||||
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)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
err = json.Unmarshal(data, &odataerr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(odataerr.Error.Code).To(Equal("generalException"))
|
||||
})
|
||||
|
||||
It("handles backend errors", func() {
|
||||
identityEducationBackend.On("GetSchools", ctx, mock.Anything).Return(nil, errorcode.New(errorcode.AccessDenied, "access denied"))
|
||||
|
||||
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)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
odataerr := libregraph.OdataError{}
|
||||
err = json.Unmarshal(data, &odataerr)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(odataerr.Error.Code).To(Equal("accessDenied"))
|
||||
})
|
||||
|
||||
It("renders an empty list of schools", func() {
|
||||
identityEducationBackend.On("GetSchools", ctx, mock.Anything).Return([]*libregraph.EducationSchool{}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools", nil)
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := service.ListResponse{}
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.Value).To(Equal([]interface{}{}))
|
||||
})
|
||||
|
||||
It("renders a list of schools", func() {
|
||||
identityEducationBackend.On("GetSchools", ctx, mock.Anything).Return([]*libregraph.EducationSchool{newSchool}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools", nil)
|
||||
svc.GetSchools(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := schoolList{}
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(res.Value)).To(Equal(1))
|
||||
Expect(res.Value[0].GetId()).To(Equal("school1"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetSchool", func() {
|
||||
It("handles missing or empty school id", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools", nil)
|
||||
svc.GetSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
|
||||
r = httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", "")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx))
|
||||
svc.GetSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
Context("with an existing school", func() {
|
||||
BeforeEach(func() {
|
||||
identityEducationBackend.On("GetSchool", mock.Anything, mock.Anything, mock.Anything).Return(newSchool, nil)
|
||||
})
|
||||
|
||||
It("gets the school", func() {
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools/"+*newSchool.Id, nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, nil), chi.RouteCtxKey, rctx))
|
||||
|
||||
svc.GetSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("PostSchool", func() {
|
||||
It("handles invalid body", func() {
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBufferString("{invalid"))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles missing display name", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetSchoolNumber("0034")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles missing school number", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetDisplayName("New School")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("disallows school create ids", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetId("disallowed")
|
||||
newSchool.SetDisplayName("New School")
|
||||
newSchool.SetSchoolNumber("0034")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles backend errors", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetDisplayName("New School")
|
||||
newSchool.SetSchoolNumber("0034")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
identityEducationBackend.On("CreateSchool", mock.Anything, mock.Anything).Return(nil, errorcode.New(errorcode.AccessDenied, "access denied"))
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusInternalServerError))
|
||||
})
|
||||
|
||||
It("creates the school", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetDisplayName("New School")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
identityEducationBackend.On("CreateSchool", mock.Anything, mock.Anything).Return(newSchool, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/", bytes.NewBuffer(newSchoolJson))
|
||||
|
||||
svc.PostSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusCreated))
|
||||
})
|
||||
})
|
||||
Describe("PatchSchool", func() {
|
||||
It("handles invalid body", func() {
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools/", bytes.NewBufferString("{invalid"))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchSchool(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles missing or empty school id", func() {
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools", nil)
|
||||
svc.PatchSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
|
||||
r = httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", "")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("handles malformed school id", func() {
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", "school%id")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PatchSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("updates the school", func() {
|
||||
newSchool = libregraph.NewEducationSchool()
|
||||
newSchool.SetDisplayName("New School Name")
|
||||
newSchoolJson, err := json.Marshal(newSchool)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools/schoolid", bytes.NewBuffer(newSchoolJson))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", "school-id")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
|
||||
svc.PatchSchool(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("DeleteSchool", func() {
|
||||
Context("with an existing school", func() {
|
||||
BeforeEach(func() {
|
||||
identityEducationBackend.On("GetSchool", mock.Anything, mock.Anything, mock.Anything).Return(newSchool, nil)
|
||||
})
|
||||
})
|
||||
|
||||
It("deletes the school", func() {
|
||||
identityEducationBackend.On("DeleteSchool", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/education/schools", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteSchool(rr, r)
|
||||
|
||||
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.SetId("user")
|
||||
identityEducationBackend.On("GetSchoolMembers", mock.Anything, mock.Anything, mock.Anything).Return([]*libregraph.User{user}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/schools/{schoolID}/members", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.GetSchoolMembers(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := ioutil.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var members []*libregraph.User
|
||||
err = json.Unmarshal(data, &members)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(len(members)).To(Equal(1))
|
||||
Expect(members[0].GetId()).To(Equal("user"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("PostSchoolMembers", func() {
|
||||
It("fails on invalid body", func() {
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/{schoolID}/members", bytes.NewBufferString("{invalid"))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PostSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("fails on missing member refs", func() {
|
||||
member := libregraph.NewMemberReference()
|
||||
data, err := json.Marshal(member)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/{schoolID}/members", bytes.NewBuffer(data))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PostSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("fails on invalid member refs", func() {
|
||||
member := libregraph.NewMemberReference()
|
||||
member.SetOdataId("/invalidtype/user")
|
||||
data, err := json.Marshal(member)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/{schoolID}/members", bytes.NewBuffer(data))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PostSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("adds a new member", func() {
|
||||
member := libregraph.NewMemberReference()
|
||||
member.SetOdataId("/users/user")
|
||||
data, err := json.Marshal(member)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
identityEducationBackend.On("AddMembersToSchool", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/schools/{schoolID}/members", bytes.NewBuffer(data))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.PostSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
|
||||
identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "AddMembersToSchool", 1)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("DeleteSchoolMembers", func() {
|
||||
It("handles missing or empty member id", func() {
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/schools/{schoolID}/members/{memberID}/$ref", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
It("handles missing or empty member id", func() {
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/schools/{schoolID}/members/{memberID}/$ref", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("memberID", "/users/user")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("deletes members", func() {
|
||||
identityEducationBackend.On("RemoveMemberFromSchool", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/schools/{schoolID}/members/{memberID}/$ref", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("schoolID", *newSchool.Id)
|
||||
rctx.URLParams.Add("memberID", "/users/user1")
|
||||
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteSchoolMember(rr, r)
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
|
||||
identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "RemoveMemberFromSchool", 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -46,6 +46,15 @@ type Service interface {
|
||||
PostGroupMember(http.ResponseWriter, *http.Request)
|
||||
DeleteGroupMember(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetSchools(http.ResponseWriter, *http.Request)
|
||||
GetSchool(http.ResponseWriter, *http.Request)
|
||||
PostSchool(http.ResponseWriter, *http.Request)
|
||||
PatchSchool(http.ResponseWriter, *http.Request)
|
||||
DeleteSchool(http.ResponseWriter, *http.Request)
|
||||
GetSchoolMembers(http.ResponseWriter, *http.Request)
|
||||
PostSchoolMember(http.ResponseWriter, *http.Request)
|
||||
DeleteSchoolMember(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)
|
||||
@@ -67,13 +76,14 @@ func NewService(opts ...Option) Service {
|
||||
m.Use(options.Middleware...)
|
||||
|
||||
svc := Graph{
|
||||
config: options.Config,
|
||||
mux: m,
|
||||
logger: &options.Logger,
|
||||
spacePropertiesCache: ttlcache.NewCache(),
|
||||
eventsPublisher: options.EventsPublisher,
|
||||
gatewayClient: options.GatewayClient,
|
||||
searchService: options.SearchService,
|
||||
config: options.Config,
|
||||
mux: m,
|
||||
logger: &options.Logger,
|
||||
spacePropertiesCache: ttlcache.NewCache(),
|
||||
eventsPublisher: options.EventsPublisher,
|
||||
gatewayClient: options.GatewayClient,
|
||||
searchService: options.SearchService,
|
||||
identityEducationBackend: options.IdentityEducationBackend,
|
||||
}
|
||||
|
||||
if options.IdentityBackend == nil {
|
||||
@@ -129,10 +139,15 @@ func NewService(opts ...Option) Service {
|
||||
TLSConfig: tlsConf,
|
||||
},
|
||||
)
|
||||
if svc.identityBackend, err = identity.NewLDAPBackend(conn, options.Config.Identity.LDAP, &options.Logger); err != nil {
|
||||
lb, err := identity.NewLDAPBackend(conn, options.Config.Identity.LDAP, &options.Logger)
|
||||
if err != nil {
|
||||
options.Logger.Error().Msgf("Error initializing LDAP Backend: '%s'", err)
|
||||
return nil
|
||||
}
|
||||
svc.identityBackend = lb
|
||||
if options.IdentityEducationBackend == nil {
|
||||
svc.identityEducationBackend = lb
|
||||
}
|
||||
default:
|
||||
options.Logger.Error().Msgf("Unknown Identity Backend: '%s'", options.Config.Identity.Backend)
|
||||
return nil
|
||||
@@ -215,6 +230,45 @@ func NewService(opts ...Option) Service {
|
||||
r.Delete("/", svc.DeleteDrive)
|
||||
})
|
||||
})
|
||||
r.With(requireAdmin).Route("/education", func(r chi.Router) {
|
||||
r.Route("/schools", func(r chi.Router) {
|
||||
r.Get("/", svc.GetSchools)
|
||||
r.Post("/", svc.PostSchool)
|
||||
r.Route("/{schoolID}", func(r chi.Router) {
|
||||
r.Get("/", svc.GetSchool)
|
||||
r.Delete("/", svc.DeleteSchool)
|
||||
r.Patch("/", svc.PatchSchool)
|
||||
r.Route("/members", func(r chi.Router) {
|
||||
r.Get("/", svc.GetSchoolMembers)
|
||||
r.Post("/$ref", svc.PostSchoolMember)
|
||||
r.Delete("/{memberID}/$ref", svc.DeleteSchoolMember)
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUsers)
|
||||
r.Post("/", svc.PostUser)
|
||||
r.Route("/{userID}", func(r chi.Router) {
|
||||
r.Get("/", svc.GetUser)
|
||||
r.Delete("/", svc.DeleteUser)
|
||||
r.Patch("/", svc.PatchUser)
|
||||
})
|
||||
})
|
||||
r.Route("/classes", func(r chi.Router) {
|
||||
r.Get("/", svc.GetGroups)
|
||||
r.Post("/", svc.PostGroup)
|
||||
r.Route("/{groupID}", func(r chi.Router) {
|
||||
r.Get("/", svc.GetGroup)
|
||||
r.Delete("/", svc.DeleteGroup)
|
||||
r.Patch("/", svc.PatchGroup)
|
||||
r.Route("/members", func(r chi.Router) {
|
||||
r.Get("/", svc.GetGroupMembers)
|
||||
r.Post("/$ref", svc.PostGroupMember)
|
||||
r.Delete("/{memberID}/$ref", svc.DeleteGroupMember)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -95,6 +95,46 @@ func (t tracing) DeleteGroupMember(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteGroupMember(w, r)
|
||||
}
|
||||
|
||||
// GetSchools implements the Service interface.
|
||||
func (t tracing) GetSchools(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetSchools(w, r)
|
||||
}
|
||||
|
||||
// GetSchool implements the Service interface.
|
||||
func (t tracing) GetSchool(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetSchool(w, r)
|
||||
}
|
||||
|
||||
// PostSchool implements the Service interface.
|
||||
func (t tracing) PostSchool(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PostSchool(w, r)
|
||||
}
|
||||
|
||||
// PatchSchool implements the Service interface.
|
||||
func (t tracing) PatchSchool(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PatchSchool(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchool implements the Service interface.
|
||||
func (t tracing) DeleteSchool(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteSchool(w, r)
|
||||
}
|
||||
|
||||
// GetSchoolMembers implements the Service interface.
|
||||
func (t tracing) GetSchoolMembers(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetSchoolMembers(w, r)
|
||||
}
|
||||
|
||||
// PostSchoolMember implements the Service interface.
|
||||
func (t tracing) PostSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.PostSchoolMember(w, r)
|
||||
}
|
||||
|
||||
// DeleteSchoolMember implements the Service interface.
|
||||
func (t tracing) DeleteSchoolMember(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteSchoolMember(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