From 6ab2f1038610ab670dd3a14504b0c462ab553817 Mon Sep 17 00:00:00 2001 From: jkoberg Date: Fri, 30 Jun 2023 11:48:57 +0200 Subject: [PATCH] only allow admins to set global notifications Signed-off-by: jkoberg --- services/userlog/pkg/command/server.go | 2 + services/userlog/pkg/server/http/option.go | 8 ++++ services/userlog/pkg/server/http/server.go | 1 + services/userlog/pkg/service/http.go | 47 +++++++++++++++++++++- services/userlog/pkg/service/options.go | 9 +++++ services/userlog/pkg/service/service.go | 9 ++++- 6 files changed, 73 insertions(+), 3 deletions(-) diff --git a/services/userlog/pkg/command/server.go b/services/userlog/pkg/command/server.go index 7417c1709..369871429 100644 --- a/services/userlog/pkg/command/server.go +++ b/services/userlog/pkg/command/server.go @@ -104,6 +104,7 @@ func Server(cfg *config.Config) *cli.Command { hClient := ehsvc.NewEventHistoryService("com.owncloud.api.eventhistory", ogrpc.DefaultClient()) vClient := settingssvc.NewValueService("com.owncloud.api.settings", ogrpc.DefaultClient()) + rClient := settingssvc.NewRoleService("com.owncloud.api.settings", ogrpc.DefaultClient()) { server, err := http.Server( @@ -116,6 +117,7 @@ func Server(cfg *config.Config) *cli.Command { http.GatewaySelector(gatewaySelector), http.History(hClient), http.Value(vClient), + http.Role(rClient), http.RegisteredEvents(_registeredEvents), ) diff --git a/services/userlog/pkg/server/http/option.go b/services/userlog/pkg/server/http/option.go index efe691101..6b55f5ba6 100644 --- a/services/userlog/pkg/server/http/option.go +++ b/services/userlog/pkg/server/http/option.go @@ -31,6 +31,7 @@ type Options struct { GatewaySelector pool.Selectable[gateway.GatewayAPIClient] HistoryClient ehsvc.EventHistoryService ValueClient settingssvc.ValueService + RoleClient settingssvc.RoleService RegisteredEvents []events.Unmarshaller } @@ -128,3 +129,10 @@ func Value(vs settingssvc.ValueService) Option { o.ValueClient = vs } } + +// Roles provides a function to configure the roles service client +func Role(rs settingssvc.RoleService) Option { + return func(o *Options) { + o.RoleClient = rs + } +} diff --git a/services/userlog/pkg/server/http/server.go b/services/userlog/pkg/server/http/server.go index 705529ed2..4fd8527d8 100644 --- a/services/userlog/pkg/server/http/server.go +++ b/services/userlog/pkg/server/http/server.go @@ -77,6 +77,7 @@ func Server(opts ...Option) (http.Service, error) { svc.HistoryClient(options.HistoryClient), svc.GatewaySelector(options.GatewaySelector), svc.ValueClient(options.ValueClient), + svc.RoleClient(options.RoleClient), svc.RegisteredEvents(options.RegisteredEvents), ) if err != nil { diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index e23f15662..277dc84d7 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -6,6 +6,10 @@ import ( "github.com/cs3org/reva/v2/pkg/ctx" revactx "github.com/cs3org/reva/v2/pkg/ctx" + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/ocis-pkg/roles" + "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode" + settings "github.com/owncloud/ocis/v2/services/settings/pkg/service/v0" ) // HeaderAcceptLanguage is the header where the client can set the locale @@ -122,8 +126,6 @@ func (ul *UserlogService) HandlePostEvent(w http.ResponseWriter, r *http.Request return } - // TODO: Check user is allowed to do this - var req PostEventsRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { ul.log.Error().Err(err).Int("returned statuscode", http.StatusBadRequest).Msg("request body is malformed") @@ -192,3 +194,44 @@ type PostEventsRequest struct { type DeprovisionData struct { DeprovisionDate string `json:"date"` } + +// RequireAdmin middleware is used to require the user in context to be an admin / have account management permissions +func RequireAdmin(rm *roles.Manager, logger log.Logger) func(next http.HandlerFunc) http.HandlerFunc { + return func(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + u, ok := revactx.ContextGetUser(r.Context()) + if !ok { + errorcode.AccessDenied.Render(w, r, http.StatusUnauthorized, "Unauthorized") + return + } + if u.Id == nil || u.Id.OpaqueId == "" { + errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "user is missing an id") + return + } + // get roles from context + roleIDs, ok := roles.ReadRoleIDsFromContext(r.Context()) + if !ok { + logger.Debug().Str("userid", u.Id.OpaqueId).Msg("No roles in context, contacting settings service") + var err error + roleIDs, err = rm.FindRoleIDsForUser(r.Context(), u.Id.OpaqueId) + if err != nil { + logger.Err(err).Str("userid", u.Id.OpaqueId).Msg("failed to get roles for user") + errorcode.AccessDenied.Render(w, r, http.StatusUnauthorized, "Unauthorized") + return + } + if len(roleIDs) == 0 { + errorcode.AccessDenied.Render(w, r, http.StatusUnauthorized, "Unauthorized") + return + } + } + + // check if permission is present in roles of the authenticated account + if rm.FindPermissionByID(r.Context(), roleIDs, settings.AccountManagementPermissionID) != nil { + next.ServeHTTP(w, r) + return + } + + errorcode.AccessDenied.Render(w, r, http.StatusForbidden, "Forbidden") + } + } +} diff --git a/services/userlog/pkg/service/options.go b/services/userlog/pkg/service/options.go index 98628cfd6..12e454ec2 100644 --- a/services/userlog/pkg/service/options.go +++ b/services/userlog/pkg/service/options.go @@ -25,6 +25,7 @@ type Options struct { HistoryClient ehsvc.EventHistoryService GatewaySelector pool.Selectable[gateway.GatewayAPIClient] ValueClient settingssvc.ValueService + RoleClient settingssvc.RoleService RegisteredEvents []events.Unmarshaller } @@ -84,8 +85,16 @@ func RegisteredEvents(e []events.Unmarshaller) Option { } } +// ValueClient adds a grpc client for the value service func ValueClient(vs settingssvc.ValueService) Option { return func(o *Options) { o.ValueClient = vs } } + +// RoleClient adds a grpc client for the role service +func RoleClient(rs settingssvc.RoleService) Option { + return func(o *Options) { + o.RoleClient = rs + } +} diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index cc102b4d4..a91727d44 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -19,6 +19,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/middleware" + "github.com/owncloud/ocis/v2/ocis-pkg/roles" ehmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/eventhistory/v0" ehsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/eventhistory/v0" settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0" @@ -80,9 +81,15 @@ func NewUserlogService(opts ...Option) (*UserlogService, error) { ul.registeredEvents[typ.String()] = e } + m := roles.NewManager( + // TODO: caching? + roles.Logger(o.Logger), + roles.RoleService(o.RoleClient), + ) + ul.m.Route("/ocs/v2.php/apps/notifications/api/v1/notifications", func(r chi.Router) { r.Get("/", ul.HandleGetEvents) - r.Post("/", ul.HandlePostEvent) + r.Post("/", RequireAdmin(&m, ul.log)(ul.HandlePostEvent)) r.Delete("/", ul.HandleDeleteEvents) if !ul.cfg.DisableSSE {