Add permission checks to users and groups Graph API

Only users with the account management permission should be able to
create, update or delete users. This also restricts access to the APIs
that allow listing all Groups/all Users.

Fixes #3177
This commit is contained in:
Ralf Haferkamp
2022-02-17 12:14:49 +01:00
parent 228a687e5e
commit e8e361d32f
3 changed files with 104 additions and 11 deletions

View File

@@ -0,0 +1,53 @@
package middleware
import (
"net/http"
revactx "github.com/cs3org/reva/pkg/ctx"
accounts "github.com/owncloud/ocis/accounts/pkg/service/v0"
"github.com/owncloud/ocis/graph/pkg/service/v0/errorcode"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/roles"
)
// 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.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(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, accounts.AccountManagementPermissionID) != nil {
next.ServeHTTP(w, r)
return
}
errorcode.AccessDenied.Render(w, r, http.StatusUnauthorized, "Unauthorized")
})
}
}

View File

@@ -5,6 +5,8 @@ import (
"github.com/owncloud/ocis/graph/pkg/config"
"github.com/owncloud/ocis/ocis-pkg/log"
"github.com/owncloud/ocis/ocis-pkg/roles"
settingssvc "github.com/owncloud/ocis/protogen/gen/ocis/services/settings/v0"
)
// Option defines a single option function.
@@ -17,6 +19,8 @@ type Options struct {
Middleware []func(http.Handler) http.Handler
GatewayClient GatewayClient
HTTPClient HTTPClient
RoleService settingssvc.RoleService
RoleManager *roles.Manager
}
// newOptions initializes the available default options.
@@ -64,3 +68,17 @@ func WithHTTPClient(val HTTPClient) Option {
o.HTTPClient = val
}
}
// RoleService provides a function to set the RoleService option.
func RoleService(val settingssvc.RoleService) Option {
return func(o *Options) {
o.RoleService = val
}
}
// RoleManager provides a function to set the RoleManager option.
func RoleManager(val *roles.Manager) Option {
return func(o *Options) {
o.RoleManager = val
}
}

View File

@@ -4,6 +4,7 @@ import (
"crypto/tls"
"net/http"
"strconv"
"time"
"github.com/ReneKroon/ttlcache/v2"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
@@ -12,8 +13,12 @@ import (
"github.com/owncloud/ocis/graph/pkg/identity"
"github.com/owncloud/ocis/graph/pkg/identity/ldap"
graphm "github.com/owncloud/ocis/graph/pkg/middleware"
"github.com/owncloud/ocis/ocis-pkg/account"
opkgm "github.com/owncloud/ocis/ocis-pkg/middleware"
"github.com/owncloud/ocis/ocis-pkg/roles"
"github.com/owncloud/ocis/ocis-pkg/service/grpc"
settingssvc "github.com/owncloud/ocis/protogen/gen/ocis/services/settings/v0"
)
const (
@@ -111,6 +116,23 @@ func NewService(opts ...Option) Service {
svc.httpClient = options.HTTPClient
}
roleService := options.RoleService
if roleService == nil {
roleService = settingssvc.NewRoleService("com.owncloud.api.settings", grpc.DefaultClient)
}
roleManager := options.RoleManager
if roleManager == nil {
m := roles.NewManager(
roles.CacheSize(1024),
roles.CacheTTL(time.Hour),
roles.Logger(options.Logger),
roles.RoleService(roleService),
)
roleManager = &m
}
requireAdmin := graphm.RequireAdmin(roleManager, options.Logger)
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
r.Use(middleware.StripSlashes)
r.Route("/v1.0", func(r chi.Router) {
@@ -120,25 +142,25 @@ func NewService(opts ...Option) Service {
r.Get("/drive/root/children", svc.GetRootDriveChildren)
})
r.Route("/users", func(r chi.Router) {
r.Get("/", svc.GetUsers)
r.Post("/", svc.PostUser)
r.With(requireAdmin).Get("/", svc.GetUsers)
r.With(requireAdmin).Post("/", svc.PostUser)
r.Route("/{userID}", func(r chi.Router) {
r.Get("/", svc.GetUser)
r.Delete("/", svc.DeleteUser)
r.Patch("/", svc.PatchUser)
r.With(requireAdmin).Delete("/", svc.DeleteUser)
r.With(requireAdmin).Patch("/", svc.PatchUser)
})
})
r.Route("/groups", func(r chi.Router) {
r.Get("/", svc.GetGroups)
r.Post("/", svc.PostGroup)
r.With(requireAdmin).Get("/", svc.GetGroups)
r.With(requireAdmin).Post("/", svc.PostGroup)
r.Route("/{groupID}", func(r chi.Router) {
r.Get("/", svc.GetGroup)
r.Delete("/", svc.DeleteGroup)
r.Patch("/", svc.PatchGroup)
r.With(requireAdmin).Delete("/", svc.DeleteGroup)
r.With(requireAdmin).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)
r.With(requireAdmin).Get("/", svc.GetGroupMembers)
r.With(requireAdmin).Post("/$ref", svc.PostGroupMember)
r.With(requireAdmin).Delete("/{memberID}/$ref", svc.DeleteGroupMember)
})
})
})