graph: add appRoleAssignments and minimal application resource (#5318)
* bump libregraph-go lib Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * add appRoleAssignment stubs Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * add get application stub Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * fetch appRoles for application from settings service Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * initial list appRoleAssignments implementation Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * initial create appRoleAssignment implementation, extract assignmentToAppRoleAssignment, configurable app id and displayname Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * initial delete appRoleAssignment implementation, changed error handling and logging Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * initial expand appRoleAssignment on users Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * test user expand appRoleAssignment Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * test appRoleAssignment Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * fix education test by actually using the mocked roleManager Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * test getapplication Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * list assignments Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * use common not exists error handling Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * default to just 'ownCloud Infinite Scale' as application name Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * fix store_test Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * roll application uuid on init Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * fix tests Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * extract method Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * Apply suggestions from code review Co-authored-by: Michael Barz <mbarz@owncloud.com> Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> Co-authored-by: Michael Barz <mbarz@owncloud.com>
This commit is contained in:
co-authored by
Michael Barz
parent
1b6c269de5
commit
078698fdf4
@@ -0,0 +1,77 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
// ListApplications implements the Service interface.
|
||||
func (g Graph) ListApplications(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling list applications")
|
||||
|
||||
lbr, err := g.roleService.ListRoles(r.Context(), &settingssvc.ListBundlesRequest{})
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not list roles: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
roles := make([]libregraph.AppRole, 0, len(lbr.Bundles))
|
||||
for _, bundle := range lbr.GetBundles() {
|
||||
role := libregraph.NewAppRole(bundle.GetId())
|
||||
role.SetDisplayName(bundle.GetDisplayName())
|
||||
roles = append(roles, *role)
|
||||
}
|
||||
|
||||
application := libregraph.NewApplication(g.config.Application.ID)
|
||||
application.SetDisplayName(g.config.Application.DisplayName)
|
||||
application.SetAppRoles(roles)
|
||||
|
||||
applications := []*libregraph.Application{
|
||||
application,
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: applications})
|
||||
}
|
||||
|
||||
// GetApplication implements the Service interface.
|
||||
func (g Graph) GetApplication(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling get application")
|
||||
|
||||
applicationID := chi.URLParam(r, "applicationID")
|
||||
|
||||
if applicationID != g.config.Application.ID {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, fmt.Sprintf("requested id %s does not match expected application id %v", applicationID, g.config.Application.ID))
|
||||
return
|
||||
}
|
||||
|
||||
lbr, err := g.roleService.ListRoles(r.Context(), &settingssvc.ListBundlesRequest{})
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not list roles: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
roles := make([]libregraph.AppRole, 0, len(lbr.Bundles))
|
||||
for _, bundle := range lbr.GetBundles() {
|
||||
role := libregraph.NewAppRole(bundle.GetId())
|
||||
role.SetDisplayName(bundle.GetDisplayName())
|
||||
roles = append(roles, *role)
|
||||
}
|
||||
|
||||
application := libregraph.NewApplication(applicationID)
|
||||
application.SetDisplayName(g.config.Application.DisplayName)
|
||||
application.SetAppRoles(roles)
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, application)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package svc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
||||
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settings "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 applicationList struct {
|
||||
Value []*libregraph.Application
|
||||
}
|
||||
|
||||
var _ = Describe("Applications", func() {
|
||||
var (
|
||||
svc service.Service
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
gatewayClient *mocks.GatewayClient
|
||||
eventsPublisher mocks.Publisher
|
||||
roleService *mocks.RoleService
|
||||
identityBackend *identitymocks.Backend
|
||||
|
||||
rr *httptest.ResponseRecorder
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
identityBackend = &identitymocks.Backend{}
|
||||
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{}
|
||||
cfg.Application.ID = "some-application-ID"
|
||||
|
||||
_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
|
||||
svc, _ = service.NewService(
|
||||
service.Config(cfg),
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
service.WithIdentityBackend(identityBackend),
|
||||
service.WithRoleService(roleService),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("ListApplications", func() {
|
||||
It("lists the configured application with appRoles", func() {
|
||||
roleService.On("ListRoles", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListBundlesResponse{
|
||||
Bundles: []*settingsmsg.Bundle{
|
||||
{
|
||||
Id: "some-appRole-ID",
|
||||
Type: settingsmsg.Bundle_TYPE_ROLE,
|
||||
DisplayName: "A human readable name for a role",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/applications", nil)
|
||||
svc.ListApplications(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseList := applicationList{}
|
||||
err = json.Unmarshal(data, &responseList)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(responseList.Value)).To(Equal(1))
|
||||
Expect(responseList.Value[0].Id).To(Equal(cfg.Application.ID))
|
||||
Expect(len(responseList.Value[0].GetAppRoles())).To(Equal(1))
|
||||
Expect(responseList.Value[0].GetAppRoles()[0].GetId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseList.Value[0].GetAppRoles()[0].GetDisplayName()).To(Equal("A human readable name for a role"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetApplication", func() {
|
||||
It("gets the application with appRoles", func() {
|
||||
roleService.On("ListRoles", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListBundlesResponse{
|
||||
Bundles: []*settingsmsg.Bundle{
|
||||
{
|
||||
Id: "some-appRole-ID",
|
||||
Type: settingsmsg.Bundle_TYPE_ROLE,
|
||||
DisplayName: "A human readable name for a role",
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/applications/some-application-ID", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("applicationID", cfg.Application.ID)
|
||||
r = r.WithContext(context.WithValue(ctx, chi.RouteCtxKey, rctx))
|
||||
svc.GetApplication(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
application := libregraph.Application{}
|
||||
err = json.Unmarshal(data, &application)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(application.Id).To(Equal(cfg.Application.ID))
|
||||
Expect(len(application.GetAppRoles())).To(Equal(1))
|
||||
Expect(application.GetAppRoles()[0].GetId()).To(Equal("some-appRole-ID"))
|
||||
Expect(application.GetAppRoles()[0].GetDisplayName()).To(Equal("A human readable name for a role"))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,129 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
|
||||
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
|
||||
)
|
||||
|
||||
const principalTypeUser = "User"
|
||||
|
||||
// ListAppRoleAssignments implements the Service interface.
|
||||
func (g Graph) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling list appRoleAssignments")
|
||||
|
||||
userID := chi.URLParam(r, "userID")
|
||||
|
||||
lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settingssvc.ListRoleAssignmentsRequest{
|
||||
AccountUuid: userID,
|
||||
})
|
||||
if err != nil {
|
||||
// TODO check the error type and return proper error code
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
|
||||
for _, assignment := range lrar.GetAssignments() {
|
||||
values = append(values, g.assignmentToAppRoleAssignment(assignment))
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, &ListResponse{Value: values})
|
||||
}
|
||||
|
||||
// CreateAppRoleAssignment implements the Service interface.
|
||||
func (g Graph) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling create appRoleAssignment")
|
||||
|
||||
appRoleAssignment := libregraph.NewAppRoleAssignmentWithDefaults()
|
||||
err := json.NewDecoder(r.Body).Decode(appRoleAssignment)
|
||||
if err != nil {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %v", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
userID := chi.URLParam(r, "userID")
|
||||
|
||||
if appRoleAssignment.GetPrincipalId() != userID {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("user id %s does not match principal id %v", userID, appRoleAssignment.GetPrincipalId()))
|
||||
return
|
||||
}
|
||||
if appRoleAssignment.GetResourceId() != g.config.Application.ID {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("resource id %s does not match expected application id %v", userID, g.config.Application.ID))
|
||||
return
|
||||
}
|
||||
|
||||
artur, err := g.roleService.AssignRoleToUser(r.Context(), &settingssvc.AssignRoleToUserRequest{
|
||||
AccountUuid: userID,
|
||||
RoleId: appRoleAssignment.AppRoleId,
|
||||
})
|
||||
if err != nil {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusCreated)
|
||||
render.JSON(w, r, g.assignmentToAppRoleAssignment(artur.GetAssignment()))
|
||||
}
|
||||
|
||||
// DeleteAppRoleAssignment implements the Service interface.
|
||||
func (g Graph) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Interface("body", r.Body).Msg("calling delete appRoleAssignment")
|
||||
|
||||
userID := chi.URLParam(r, "userID")
|
||||
|
||||
// check assignment belongs to the user
|
||||
lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settingssvc.ListRoleAssignmentsRequest{
|
||||
AccountUuid: userID,
|
||||
})
|
||||
if err != nil {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
appRoleAssignmentID := chi.URLParam(r, "appRoleAssignmentID")
|
||||
|
||||
assignmentFound := false
|
||||
for _, roleAssignment := range lrar.GetAssignments() {
|
||||
if roleAssignment.Id == appRoleAssignmentID {
|
||||
assignmentFound = true
|
||||
}
|
||||
}
|
||||
if !assignmentFound {
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, fmt.Sprintf("appRoleAssignment %v not found for user %v", appRoleAssignmentID, userID))
|
||||
return
|
||||
}
|
||||
|
||||
_, err = g.roleService.RemoveRoleFromUser(r.Context(), &settingssvc.RemoveRoleFromUserRequest{
|
||||
Id: appRoleAssignmentID,
|
||||
})
|
||||
if err != nil {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
render.NoContent(w, r)
|
||||
}
|
||||
|
||||
func (g Graph) assignmentToAppRoleAssignment(assignment *settingsmsg.UserRoleAssignment) libregraph.AppRoleAssignment {
|
||||
appRoleAssignment := libregraph.NewAppRoleAssignmentWithDefaults()
|
||||
appRoleAssignment.SetId(assignment.Id)
|
||||
appRoleAssignment.SetAppRoleId(assignment.RoleId)
|
||||
appRoleAssignment.SetPrincipalType(principalTypeUser) // currently always assigned to the user
|
||||
appRoleAssignment.SetResourceId(g.config.Application.ID)
|
||||
appRoleAssignment.SetResourceDisplayName(g.config.Application.DisplayName)
|
||||
appRoleAssignment.SetPrincipalId(assignment.AccountUuid)
|
||||
// appRoleAssignment.SetPrincipalDisplayName() // TODO fetch and cache
|
||||
return *appRoleAssignment
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package svc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
revactx "github.com/cs3org/reva/v2/pkg/ctx"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
. "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"
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settings "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 assignmentList struct {
|
||||
Value []*libregraph.AppRoleAssignment
|
||||
}
|
||||
|
||||
var _ = Describe("AppRoleAssignments", func() {
|
||||
var (
|
||||
svc service.Service
|
||||
ctx context.Context
|
||||
cfg *config.Config
|
||||
gatewayClient *mocks.GatewayClient
|
||||
eventsPublisher mocks.Publisher
|
||||
roleService *mocks.RoleService
|
||||
identityBackend *identitymocks.Backend
|
||||
|
||||
rr *httptest.ResponseRecorder
|
||||
|
||||
currentUser = &userv1beta1.User{
|
||||
Id: &userv1beta1.UserId{
|
||||
OpaqueId: "user",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
identityBackend = &identitymocks.Backend{}
|
||||
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{}
|
||||
cfg.Application.ID = "some-application-ID"
|
||||
|
||||
_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
|
||||
svc, _ = service.NewService(
|
||||
service.Config(cfg),
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
service.WithIdentityBackend(identityBackend),
|
||||
service.WithRoleService(roleService),
|
||||
)
|
||||
})
|
||||
|
||||
Describe("ListAppRoleAssignments", func() {
|
||||
It("lists the appRoleAssignments", func() {
|
||||
user := &libregraph.User{
|
||||
Id: libregraph.PtrString("user1"),
|
||||
}
|
||||
assignments := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: user.GetId(),
|
||||
RoleId: "some-appRole-ID",
|
||||
},
|
||||
}
|
||||
roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1/appRoleAssignments", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.ListAppRoleAssignments(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseList := assignmentList{}
|
||||
err = json.Unmarshal(data, &responseList)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(responseList.Value)).To(Equal(1))
|
||||
Expect(responseList.Value[0].GetId()).ToNot(BeEmpty())
|
||||
Expect(responseList.Value[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseList.Value[0].GetPrincipalId()).To(Equal(user.GetId()))
|
||||
Expect(responseList.Value[0].GetResourceId()).To(Equal(cfg.Application.ID))
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Describe("CreateAppRoleAssignment", func() {
|
||||
It("creates an appRoleAssignment", func() {
|
||||
user := &libregraph.User{
|
||||
Id: libregraph.PtrString("user1"),
|
||||
}
|
||||
userRoleAssignment := &settingsmsg.UserRoleAssignment{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: user.GetId(),
|
||||
RoleId: "some-appRole-ID",
|
||||
}
|
||||
roleService.On("AssignRoleToUser", mock.Anything, mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{Assignment: userRoleAssignment}, nil)
|
||||
|
||||
ara := libregraph.NewAppRoleAssignmentWithDefaults()
|
||||
ara.SetAppRoleId("some-appRole-ID")
|
||||
ara.SetPrincipalId(user.GetId())
|
||||
ara.SetResourceId(cfg.Application.ID)
|
||||
|
||||
araJson, err := json.Marshal(ara)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users/user1/appRoleAssignments", bytes.NewBuffer(araJson))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.CreateAppRoleAssignment(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusCreated))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
assignment := libregraph.AppRoleAssignment{}
|
||||
err = json.Unmarshal(data, &assignment)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(assignment.GetId()).ToNot(BeEmpty())
|
||||
Expect(assignment.GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(assignment.GetPrincipalId()).To(Equal("user1"))
|
||||
Expect(assignment.GetResourceId()).To(Equal(cfg.Application.ID))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
Describe("DeleteAppRoleAssignment", func() {
|
||||
It("deletes an appRoleAssignment", func() {
|
||||
user := &libregraph.User{
|
||||
Id: libregraph.PtrString("user1"),
|
||||
}
|
||||
|
||||
assignments := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: user.GetId(),
|
||||
RoleId: "some-appRole-ID",
|
||||
},
|
||||
}
|
||||
roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
|
||||
|
||||
roleService.On("RemoveRoleFromUser", mock.Anything, mock.Anything, mock.Anything).Return(&empty.Empty{}, nil)
|
||||
|
||||
ara := libregraph.NewAppRoleAssignmentWithDefaults()
|
||||
ara.SetAppRoleId("some-appRole-ID")
|
||||
ara.SetPrincipalId(user.GetId())
|
||||
ara.SetResourceId(cfg.Application.ID)
|
||||
|
||||
araJson, err := json.Marshal(ara)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/users/user1/appRoleAssignments/some-appRoleAssignment-ID", bytes.NewBuffer(araJson))
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
rctx.URLParams.Add("appRoleAssignmentID", "some-appRoleAssignment-ID")
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.DeleteAppRoleAssignment(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusNoContent))
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
@@ -74,7 +74,7 @@ var _ = Describe("EducationUsers", func() {
|
||||
service.WithGatewayClient(gatewayClient),
|
||||
service.EventsPublisher(&eventsPublisher),
|
||||
service.WithIdentityEducationBackend(identityEducationBackend),
|
||||
//service.WithRoleService(roleService),
|
||||
service.WithRoleService(roleService),
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -24,6 +24,16 @@ func (i instrument) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// ListApplications implements the Service interface.
|
||||
func (i instrument) ListApplications(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.ListApplications(w, r)
|
||||
}
|
||||
|
||||
// GetApplication implements the Service interface.
|
||||
func (i instrument) GetApplication(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetApplication(w, r)
|
||||
}
|
||||
|
||||
// GetMe implements the Service interface.
|
||||
func (i instrument) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetMe(w, r)
|
||||
@@ -59,6 +69,21 @@ func (i instrument) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.ChangeOwnPassword(w, r)
|
||||
}
|
||||
|
||||
// ListAppRoleAssignments implements the Service interface.
|
||||
func (i instrument) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.ListAppRoleAssignments(w, r)
|
||||
}
|
||||
|
||||
// CreateAppRoleAssignment implements the Service interface.
|
||||
func (i instrument) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.CreateAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// DeleteAppRoleAssignment implements the Service interface.
|
||||
func (i instrument) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.DeleteAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// GetGroups implements the Service interface.
|
||||
func (i instrument) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
i.next.GetGroups(w, r)
|
||||
|
||||
@@ -24,6 +24,16 @@ func (l logging) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// ListApplications implements the Service interface.
|
||||
func (l logging) ListApplications(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.ListApplications(w, r)
|
||||
}
|
||||
|
||||
// GetApplication implements the Service interface.
|
||||
func (l logging) GetApplication(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetApplication(w, r)
|
||||
}
|
||||
|
||||
// GetMe implements the Service interface.
|
||||
func (l logging) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetMe(w, r)
|
||||
@@ -59,6 +69,21 @@ func (l logging) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.ChangeOwnPassword(w, r)
|
||||
}
|
||||
|
||||
// ListAppRoleAssignments implements the Service interface.
|
||||
func (l logging) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.ListAppRoleAssignments(w, r)
|
||||
}
|
||||
|
||||
// CreateAppRoleAssignment implements the Service interface.
|
||||
func (l logging) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.CreateAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// DeleteAppRoleAssignment implements the Service interface.
|
||||
func (l logging) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.DeleteAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// GetGroups implements the Service interface.
|
||||
func (l logging) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
l.next.GetGroups(w, r)
|
||||
|
||||
@@ -32,6 +32,10 @@ const (
|
||||
// Service defines the service handlers.
|
||||
type Service interface {
|
||||
ServeHTTP(http.ResponseWriter, *http.Request)
|
||||
|
||||
ListApplications(w http.ResponseWriter, r *http.Request)
|
||||
GetApplication(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetMe(http.ResponseWriter, *http.Request)
|
||||
GetUsers(http.ResponseWriter, *http.Request)
|
||||
GetUser(http.ResponseWriter, *http.Request)
|
||||
@@ -40,6 +44,10 @@ type Service interface {
|
||||
PatchUser(http.ResponseWriter, *http.Request)
|
||||
ChangeOwnPassword(http.ResponseWriter, *http.Request)
|
||||
|
||||
ListAppRoleAssignments(http.ResponseWriter, *http.Request)
|
||||
CreateAppRoleAssignment(http.ResponseWriter, *http.Request)
|
||||
DeleteAppRoleAssignment(http.ResponseWriter, *http.Request)
|
||||
|
||||
GetGroups(http.ResponseWriter, *http.Request)
|
||||
GetGroup(http.ResponseWriter, *http.Request)
|
||||
PostGroup(http.ResponseWriter, *http.Request)
|
||||
@@ -127,80 +135,8 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
identityEducationBackend: options.IdentityEducationBackend,
|
||||
}
|
||||
|
||||
if options.IdentityBackend == nil {
|
||||
switch options.Config.Identity.Backend {
|
||||
case "cs3":
|
||||
svc.identityBackend = &identity.CS3{
|
||||
Config: options.Config.Reva,
|
||||
Logger: &options.Logger,
|
||||
}
|
||||
case "ldap":
|
||||
var err error
|
||||
|
||||
var tlsConf *tls.Config
|
||||
if options.Config.Identity.LDAP.Insecure {
|
||||
// When insecure is set to true then we don't need a certificate.
|
||||
options.Config.Identity.LDAP.CACert = ""
|
||||
tlsConf = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
//nolint:gosec // We need the ability to run with "insecure" (dev/testing)
|
||||
InsecureSkipVerify: options.Config.Identity.LDAP.Insecure,
|
||||
}
|
||||
}
|
||||
|
||||
if options.Config.Identity.LDAP.CACert != "" {
|
||||
if err := ocisldap.WaitForCA(options.Logger,
|
||||
options.Config.Identity.LDAP.Insecure,
|
||||
options.Config.Identity.LDAP.CACert); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("The configured LDAP CA cert does not exist")
|
||||
}
|
||||
if tlsConf == nil {
|
||||
tlsConf = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
certs := x509.NewCertPool()
|
||||
pemData, err := os.ReadFile(options.Config.Identity.LDAP.CACert)
|
||||
if err != nil {
|
||||
options.Logger.Error().Err(err).Msgf("Error initializing LDAP Backend")
|
||||
return svc, err
|
||||
}
|
||||
if !certs.AppendCertsFromPEM(pemData) {
|
||||
options.Logger.Error().Msgf("Error initializing LDAP Backend. Adding CA cert failed")
|
||||
return svc, err
|
||||
}
|
||||
tlsConf.RootCAs = certs
|
||||
}
|
||||
|
||||
conn := ldap.NewLDAPWithReconnect(&options.Logger,
|
||||
ldap.Config{
|
||||
URI: options.Config.Identity.LDAP.URI,
|
||||
BindDN: options.Config.Identity.LDAP.BindDN,
|
||||
BindPassword: options.Config.Identity.LDAP.BindPassword,
|
||||
TLSConfig: tlsConf,
|
||||
},
|
||||
)
|
||||
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 svc, err
|
||||
}
|
||||
svc.identityBackend = lb
|
||||
if options.IdentityEducationBackend == nil {
|
||||
if options.Config.Identity.LDAP.EducationResourcesEnabled {
|
||||
svc.identityEducationBackend = lb
|
||||
} else {
|
||||
errEduBackend := &identity.ErrEducationBackend{}
|
||||
svc.identityEducationBackend = errEduBackend
|
||||
}
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("Unknown Identity Backend: '%s'", options.Config.Identity.Backend)
|
||||
options.Logger.Err(err)
|
||||
return svc, err
|
||||
}
|
||||
} else {
|
||||
svc.identityBackend = options.IdentityBackend
|
||||
if err := setIdentityBackends(options, &svc); err != nil {
|
||||
return svc, err
|
||||
}
|
||||
|
||||
if options.PermissionService == nil {
|
||||
@@ -209,6 +145,12 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
svc.permissionsService = options.PermissionService
|
||||
}
|
||||
|
||||
if options.RoleService == nil {
|
||||
svc.roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient())
|
||||
} else {
|
||||
svc.roleService = options.RoleService
|
||||
}
|
||||
|
||||
roleManager := options.RoleManager
|
||||
if roleManager == nil {
|
||||
storeOptions := store.OcisStoreOptions{
|
||||
@@ -239,6 +181,10 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
r.Put("/tags", svc.AssignTags)
|
||||
r.Delete("/tags", svc.UnassignTags)
|
||||
})
|
||||
r.Route("/applications", func(r chi.Router) {
|
||||
r.Get("/", svc.ListApplications)
|
||||
r.Get("/{applicationID}", svc.GetApplication)
|
||||
})
|
||||
r.Route("/me", func(r chi.Router) {
|
||||
r.Get("/", svc.GetMe)
|
||||
r.Get("/drives", svc.GetDrives)
|
||||
@@ -252,6 +198,11 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
r.Get("/", svc.GetUser)
|
||||
r.With(requireAdmin).Delete("/", svc.DeleteUser)
|
||||
r.With(requireAdmin).Patch("/", svc.PatchUser)
|
||||
r.With(requireAdmin).Route("/appRoleAssignments", func(r chi.Router) {
|
||||
r.Get("/", svc.ListAppRoleAssignments)
|
||||
r.Post("/", svc.CreateAppRoleAssignment)
|
||||
r.Delete("/{appRoleAssignmentID}", svc.DeleteAppRoleAssignment)
|
||||
})
|
||||
})
|
||||
})
|
||||
r.Route("/groups", func(r chi.Router) {
|
||||
@@ -322,6 +273,88 @@ func NewService(opts ...Option) (Graph, error) {
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func setIdentityBackends(options Options, svc *Graph) error {
|
||||
if options.IdentityBackend == nil {
|
||||
switch options.Config.Identity.Backend {
|
||||
case "cs3":
|
||||
svc.identityBackend = &identity.CS3{
|
||||
Config: options.Config.Reva,
|
||||
Logger: &options.Logger,
|
||||
}
|
||||
case "ldap":
|
||||
var err error
|
||||
|
||||
var tlsConf *tls.Config
|
||||
if options.Config.Identity.LDAP.Insecure {
|
||||
|
||||
// When insecure is set to true then we don't need a certificate.
|
||||
options.Config.Identity.LDAP.CACert = ""
|
||||
tlsConf = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
|
||||
//nolint:gosec // We need the ability to run with "insecure" (dev/testing)
|
||||
InsecureSkipVerify: options.Config.Identity.LDAP.Insecure,
|
||||
}
|
||||
}
|
||||
|
||||
if options.Config.Identity.LDAP.CACert != "" {
|
||||
if err := ocisldap.WaitForCA(options.Logger,
|
||||
options.Config.Identity.LDAP.Insecure,
|
||||
options.Config.Identity.LDAP.CACert); err != nil {
|
||||
options.Logger.Fatal().Err(err).Msg("The configured LDAP CA cert does not exist")
|
||||
}
|
||||
if tlsConf == nil {
|
||||
tlsConf = &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
}
|
||||
certs := x509.NewCertPool()
|
||||
pemData, err := os.ReadFile(options.Config.Identity.LDAP.CACert)
|
||||
if err != nil {
|
||||
options.Logger.Error().Err(err).Msg("Error initializing LDAP Backend")
|
||||
return err
|
||||
}
|
||||
if !certs.AppendCertsFromPEM(pemData) {
|
||||
options.Logger.Error().Msg("Error initializing LDAP Backend. Adding CA cert failed")
|
||||
return err
|
||||
}
|
||||
tlsConf.RootCAs = certs
|
||||
}
|
||||
|
||||
conn := ldap.NewLDAPWithReconnect(&options.Logger,
|
||||
ldap.Config{
|
||||
URI: options.Config.Identity.LDAP.URI,
|
||||
BindDN: options.Config.Identity.LDAP.BindDN,
|
||||
BindPassword: options.Config.Identity.LDAP.BindPassword,
|
||||
TLSConfig: tlsConf,
|
||||
},
|
||||
)
|
||||
lb, err := identity.NewLDAPBackend(conn, options.Config.Identity.LDAP, &options.Logger)
|
||||
if err != nil {
|
||||
options.Logger.Error().Err(err).Msg("Error initializing LDAP Backend")
|
||||
return err
|
||||
}
|
||||
svc.identityBackend = lb
|
||||
if options.IdentityEducationBackend == nil {
|
||||
if options.Config.Identity.LDAP.EducationResourcesEnabled {
|
||||
svc.identityEducationBackend = lb
|
||||
} else {
|
||||
errEduBackend := &identity.ErrEducationBackend{}
|
||||
svc.identityEducationBackend = errEduBackend
|
||||
}
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("unknown identity backend: '%s'", options.Config.Identity.Backend)
|
||||
options.Logger.Err(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
svc.identityBackend = options.IdentityBackend
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseHeaderPurge parses the 'Purge' header.
|
||||
// '1', 't', 'T', 'TRUE', 'true', 'True' are parsed as true
|
||||
// all other values are false.
|
||||
|
||||
@@ -20,6 +20,16 @@ func (t tracing) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// ListApplications implements the Service interface.
|
||||
func (t tracing) ListApplications(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.ListApplications(w, r)
|
||||
}
|
||||
|
||||
// GetApplication implements the Service interface.
|
||||
func (t tracing) GetApplication(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetApplication(w, r)
|
||||
}
|
||||
|
||||
// GetMe implements the Service interface.
|
||||
func (t tracing) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetMe(w, r)
|
||||
@@ -55,6 +65,21 @@ func (t tracing) ChangeOwnPassword(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.ChangeOwnPassword(w, r)
|
||||
}
|
||||
|
||||
// ListAppRoleAssignments implements the Service interface.
|
||||
func (t tracing) ListAppRoleAssignments(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.ListAppRoleAssignments(w, r)
|
||||
}
|
||||
|
||||
// CreateAppRoleAssignment implements the Service interface.
|
||||
func (t tracing) CreateAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.CreateAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// DeleteAppRoleAssignment implements the Service interface.
|
||||
func (t tracing) DeleteAppRoleAssignment(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.DeleteAppRoleAssignment(w, r)
|
||||
}
|
||||
|
||||
// GetGroups implements the Service interface.
|
||||
func (t tracing) GetGroups(w http.ResponseWriter, r *http.Request) {
|
||||
t.next.GetGroups(w, r)
|
||||
|
||||
@@ -58,6 +58,30 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// expand appRoleAssignments if requested
|
||||
if slices.Contains(exp, "appRoleAssignments") {
|
||||
lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
|
||||
AccountUuid: me.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("userid", me.GetId()).Msg("could not get appRoleAssignments for self")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
|
||||
for _, assignment := range lrar.GetAssignments() {
|
||||
values = append(values, g.assignmentToAppRoleAssignment(assignment))
|
||||
}
|
||||
me.AppRoleAssignments = values
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, me)
|
||||
}
|
||||
@@ -87,6 +111,26 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
// expand appRoleAssignments if requested
|
||||
exp := strings.Split(r.URL.Query().Get("$expand"), ",")
|
||||
if slices.Contains(exp, "appRoleAssignments") {
|
||||
for _, u := range users {
|
||||
lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
|
||||
AccountUuid: u.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("userid", u.GetId()).Msg("could not get appRoleAssignments when listing user")
|
||||
continue
|
||||
}
|
||||
|
||||
values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
|
||||
for _, assignment := range lrar.GetAssignments() {
|
||||
values = append(values, g.assignmentToAppRoleAssignment(assignment))
|
||||
}
|
||||
u.AppRoleAssignments = values
|
||||
}
|
||||
}
|
||||
|
||||
users, err = sortUsers(odataReq, users)
|
||||
if err != nil {
|
||||
logger.Debug().Interface("query", odataReq).Msg("error while sorting users according to query")
|
||||
@@ -274,6 +318,29 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// expand appRoleAssignments if requested
|
||||
if slices.Contains(exp, "appRoleAssignments") {
|
||||
|
||||
lrar, err := g.roleService.ListRoleAssignments(r.Context(), &settings.ListRoleAssignmentsRequest{
|
||||
AccountUuid: user.GetId(),
|
||||
})
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("userid", user.GetId()).Msg("could not get appRoleAssignments for user")
|
||||
var errcode errorcode.Error
|
||||
if errors.As(err, &errcode) {
|
||||
errcode.Render(w, r)
|
||||
} else {
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
values := make([]libregraph.AppRoleAssignment, 0, len(lrar.GetAssignments()))
|
||||
for _, assignment := range lrar.GetAssignments() {
|
||||
values = append(values, g.assignmentToAppRoleAssignment(assignment))
|
||||
}
|
||||
user.AppRoleAssignments = values
|
||||
}
|
||||
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, user)
|
||||
|
||||
@@ -17,11 +17,13 @@ import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"go-micro.dev/v4/client"
|
||||
|
||||
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"
|
||||
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
|
||||
settings "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"
|
||||
@@ -67,6 +69,7 @@ var _ = Describe("Users", func() {
|
||||
cfg.TokenManager.JWTSecret = "loremipsum"
|
||||
cfg.Commons = &shared.Commons{}
|
||||
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
|
||||
cfg.Application.ID = "some-application-ID"
|
||||
|
||||
_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
|
||||
svc, _ = service.NewService(
|
||||
@@ -94,8 +97,13 @@ var _ = Describe("Users", func() {
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
})
|
||||
|
||||
It("expands the user", func() {
|
||||
user := &libregraph.User{}
|
||||
It("expands the memberOf", func() {
|
||||
user := &libregraph.User{
|
||||
Id: libregraph.PtrString("user1"),
|
||||
MemberOf: []libregraph.Group{
|
||||
{DisplayName: libregraph.PtrString("somegroup")},
|
||||
},
|
||||
}
|
||||
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=memberOf", nil)
|
||||
@@ -103,6 +111,48 @@ var _ = Describe("Users", func() {
|
||||
svc.GetMe(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseUser := &libregraph.User{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(responseUser.GetMemberOf()).To(HaveLen(1))
|
||||
Expect(responseUser.GetMemberOf()[0].GetDisplayName()).To(Equal("somegroup"))
|
||||
|
||||
})
|
||||
|
||||
It("expands the appRoleAssignments", func() {
|
||||
assignments := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: "user",
|
||||
RoleId: "some-appRole-ID",
|
||||
},
|
||||
}
|
||||
roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=appRoleAssignments", nil)
|
||||
r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
|
||||
svc.GetMe(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseUser := &libregraph.User{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(responseUser.GetId()).To(Equal("user"))
|
||||
Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -216,6 +266,62 @@ var _ = Describe("Users", func() {
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest))
|
||||
})
|
||||
|
||||
It("expands the appRoleAssignments", func() {
|
||||
|
||||
user := &libregraph.User{}
|
||||
user.SetId("user1")
|
||||
user.SetMail("z@example.com")
|
||||
user.SetDisplayName("9")
|
||||
user.SetOnPremisesSamAccountName("9")
|
||||
user2 := &libregraph.User{}
|
||||
user2.SetId("user2")
|
||||
user2.SetMail("a@example.com")
|
||||
user2.SetDisplayName("1")
|
||||
user2.SetOnPremisesSamAccountName("1")
|
||||
users := []*libregraph.User{user, user2}
|
||||
identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
|
||||
|
||||
roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(func(ctx context.Context, in *settings.ListRoleAssignmentsRequest, opts ...client.CallOption) *settings.ListRoleAssignmentsResponse {
|
||||
return &settings.ListRoleAssignmentsResponse{Assignments: []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: in.GetAccountUuid(),
|
||||
RoleId: "some-appRole-ID",
|
||||
},
|
||||
}}
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$expand=appRoleAssignments", nil)
|
||||
r = r.WithContext(revactx.ContextSetUser(ctx, currentUser))
|
||||
svc.GetUsers(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res := userList{}
|
||||
err = json.Unmarshal(data, &res)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseUsers := res.Value
|
||||
Expect(len(responseUsers)).To(Equal(2))
|
||||
Expect(responseUsers[0].GetId()).To(Equal("user1"))
|
||||
Expect(responseUsers[0].GetAppRoleAssignments()).To(HaveLen(1))
|
||||
Expect(responseUsers[0].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
|
||||
Expect(responseUsers[0].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseUsers[0].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
|
||||
Expect(responseUsers[0].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
|
||||
|
||||
Expect(responseUsers[1].GetId()).To(Equal("user2"))
|
||||
Expect(responseUsers[1].GetAppRoleAssignments()).To(HaveLen(1))
|
||||
Expect(responseUsers[1].GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
|
||||
Expect(responseUsers[1].GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseUsers[1].GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user2"))
|
||||
Expect(responseUsers[1].GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetUser", func() {
|
||||
@@ -322,6 +428,44 @@ var _ = Describe("Users", func() {
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(len(responseUser.GetDrives())).To(Equal(1))
|
||||
})
|
||||
|
||||
It("expands the appRoleAssignments", func() {
|
||||
user := &libregraph.User{}
|
||||
user.SetId("user1")
|
||||
|
||||
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
|
||||
|
||||
assignments := []*settingsmsg.UserRoleAssignment{
|
||||
{
|
||||
Id: "some-appRoleAssignment-ID",
|
||||
AccountUuid: "user1",
|
||||
RoleId: "some-appRole-ID",
|
||||
},
|
||||
}
|
||||
roleService.On("ListRoleAssignments", mock.Anything, mock.Anything, mock.Anything).Return(&settings.ListRoleAssignmentsResponse{Assignments: assignments}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users/user1?$expand=appRoleAssignments", nil)
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("userID", user.GetId())
|
||||
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
|
||||
svc.GetUser(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
data, err := io.ReadAll(rr.Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
responseUser := &libregraph.User{}
|
||||
err = json.Unmarshal(data, &responseUser)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(responseUser.GetId()).To(Equal("user1"))
|
||||
Expect(responseUser.GetAppRoleAssignments()).To(HaveLen(1))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetId()).To(Equal("some-appRoleAssignment-ID"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetAppRoleId()).To(Equal("some-appRole-ID"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetPrincipalId()).To(Equal("user1"))
|
||||
Expect(responseUser.GetAppRoleAssignments()[0].GetResourceId()).To(Equal("some-application-ID"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("PostUser", func() {
|
||||
@@ -381,7 +525,7 @@ var _ = Describe("Users", func() {
|
||||
})
|
||||
|
||||
It("creates a user", func() {
|
||||
roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settingssvc.AssignRoleToUserResponse{}, nil)
|
||||
roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settings.AssignRoleToUserResponse{}, nil)
|
||||
identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
|
||||
user.SetId("/users/user")
|
||||
return &user
|
||||
|
||||
Reference in New Issue
Block a user