diff --git a/changelog/unreleased/blocked-users.md b/changelog/unreleased/blocked-users.md new file mode 100644 index 0000000000..eaf3f0db3b --- /dev/null +++ b/changelog/unreleased/blocked-users.md @@ -0,0 +1,6 @@ +Enhancement: Block users + +Allows an operator to set a list of users that +are banned for every operation in reva. + +https://github.com/cs3org/reva/pull/3402 diff --git a/internal/grpc/interceptors/auth/auth.go b/internal/grpc/interceptors/auth/auth.go index 1d2015dea3..4b97bd1e35 100644 --- a/internal/grpc/interceptors/auth/auth.go +++ b/internal/grpc/interceptors/auth/auth.go @@ -33,6 +33,7 @@ import ( "github.com/cs3org/reva/pkg/sharedconf" "github.com/cs3org/reva/pkg/token" tokenmgr "github.com/cs3org/reva/pkg/token/manager/registry" + "github.com/cs3org/reva/pkg/user" "github.com/cs3org/reva/pkg/utils" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" @@ -50,6 +51,7 @@ type config struct { TokenManager string `mapstructure:"token_manager"` TokenManagers map[string]map[string]interface{} `mapstructure:"token_managers"` GatewayAddr string `mapstructure:"gateway_addr"` + blockedUsers []string } func parseConfig(m map[string]interface{}) (*config, error) { @@ -58,6 +60,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { err = errors.Wrap(err, "auth: error decoding conf") return nil, err } + c.blockedUsers = sharedconf.GetBlockedUsers() return c, nil } @@ -70,6 +73,8 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI return nil, err } + blockedUsers := user.NewBlockedUsersSet(conf.blockedUsers) + if conf.TokenManager == "" { conf.TokenManager = "jwt" } @@ -100,6 +105,9 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI if ok { u, err := dismantleToken(ctx, tkn, req, tokenManager, conf.GatewayAddr, true) if err == nil { + if blockedUsers.IsBlocked(u.Username) { + return nil, status.Errorf(codes.PermissionDenied, "user %s blocked", u.Username) + } ctx = ctxpkg.ContextSetUser(ctx, u) } } @@ -120,6 +128,10 @@ func NewUnary(m map[string]interface{}, unprotected []string) (grpc.UnaryServerI return nil, status.Errorf(codes.PermissionDenied, "auth: core access token is invalid") } + if blockedUsers.IsBlocked(u.Username) { + return nil, status.Errorf(codes.PermissionDenied, "user %s blocked", u.Username) + } + ctx = ctxpkg.ContextSetUser(ctx, u) return handler(ctx, req) } diff --git a/internal/grpc/services/authprovider/authprovider.go b/internal/grpc/services/authprovider/authprovider.go index ae4c7c1050..2b819c8217 100644 --- a/internal/grpc/services/authprovider/authprovider.go +++ b/internal/grpc/services/authprovider/authprovider.go @@ -31,6 +31,8 @@ import ( "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/sharedconf" + "github.com/cs3org/reva/pkg/user" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "google.golang.org/grpc" @@ -43,18 +45,21 @@ func init() { type config struct { AuthManager string `mapstructure:"auth_manager"` AuthManagers map[string]map[string]interface{} `mapstructure:"auth_managers"` + blockedUsers []string } func (c *config) init() { if c.AuthManager == "" { c.AuthManager = "json" } + c.blockedUsers = sharedconf.GetBlockedUsers() } type service struct { - authmgr auth.Manager - conf *config - plugin *plugin.RevaPlugin + authmgr auth.Manager + conf *config + plugin *plugin.RevaPlugin + blockedUsers user.BlockedUsers } func parseConfig(m map[string]interface{}) (*config, error) { @@ -107,9 +112,10 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { } svc := &service{ - conf: c, - authmgr: authManager, - plugin: plug, + conf: c, + authmgr: authManager, + plugin: plug, + blockedUsers: user.NewBlockedUsersSet(c.blockedUsers), } return svc, nil @@ -135,6 +141,12 @@ func (s *service) Authenticate(ctx context.Context, req *provider.AuthenticateRe username := req.ClientId password := req.ClientSecret + if s.blockedUsers.IsBlocked(username) { + return &provider.AuthenticateResponse{ + Status: status.NewPermissionDenied(ctx, errtypes.PermissionDenied(""), "user is blocked"), + }, nil + } + u, scope, err := s.authmgr.Authenticate(ctx, username, password) switch v := err.(type) { case nil: diff --git a/pkg/sharedconf/sharedconf.go b/pkg/sharedconf/sharedconf.go index d458c9bc22..784ab46ea0 100644 --- a/pkg/sharedconf/sharedconf.go +++ b/pkg/sharedconf/sharedconf.go @@ -28,10 +28,11 @@ import ( var sharedConf = &conf{} type conf struct { - JWTSecret string `mapstructure:"jwt_secret"` - GatewaySVC string `mapstructure:"gatewaysvc"` - DataGateway string `mapstructure:"datagateway"` - SkipUserGroupsInToken bool `mapstructure:"skip_user_groups_in_token"` + JWTSecret string `mapstructure:"jwt_secret"` + GatewaySVC string `mapstructure:"gatewaysvc"` + DataGateway string `mapstructure:"datagateway"` + SkipUserGroupsInToken bool `mapstructure:"skip_user_groups_in_token"` + BlockedUsers []string `mapstructure:"blocked_users"` } // Decode decodes the configuration. @@ -92,3 +93,8 @@ func GetDataGateway(val string) string { func SkipUserGroupsInToken() bool { return sharedConf.SkipUserGroupsInToken } + +// GetBlockedUsers returns a list of blocked users +func GetBlockedUsers() []string { + return sharedConf.BlockedUsers +} diff --git a/pkg/user/blocked.go b/pkg/user/blocked.go new file mode 100644 index 0000000000..298a02b56c --- /dev/null +++ b/pkg/user/blocked.go @@ -0,0 +1,37 @@ +// Copyright 2018-2022 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package user + +// BlockedUsers is a set containing all the blocked users +type BlockedUsers map[string]struct{} + +// NewBlockedUsersSet creates a new set of blocked users from a list +func NewBlockedUsersSet(users []string) BlockedUsers { + s := make(map[string]struct{}) + for _, u := range users { + s[u] = struct{}{} + } + return s +} + +// IsBlocked returns true if the user is blocked +func (b BlockedUsers) IsBlocked(user string) bool { + _, ok := b[user] + return ok +}