cosmos-sdk/x/circuit/keeper/msg_server.go
2024-06-10 09:07:31 +00:00

201 lines
6.2 KiB
Go

package keeper
import (
"bytes"
"context"
"fmt"
"strings"
"cosmossdk.io/collections"
"cosmossdk.io/core/event"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/circuit/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
type msgServer struct {
Keeper
}
var _ types.MsgServer = msgServer{}
// NewMsgServerImpl returns an implementation of the circuit MsgServer interface
// for the provided Keeper.
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{Keeper: keeper}
}
func (srv msgServer) AuthorizeCircuitBreaker(ctx context.Context, msg *types.MsgAuthorizeCircuitBreaker) (*types.MsgAuthorizeCircuitBreakerResponse, error) {
address, err := srv.addressCodec.StringToBytes(msg.Granter)
if err != nil {
return nil, err
}
// if the granter is the module authority no need to check perms
if !bytes.Equal(address, srv.GetAuthority()) {
// Check that the authorizer has the permission level of "super admin"
perms, err := srv.Permissions.Get(ctx, address)
if err != nil {
if errorsmod.IsOf(err, collections.ErrNotFound) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only super admins can authorize users")
}
return nil, err
}
if perms.Level != types.Permissions_LEVEL_SUPER_ADMIN {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only super admins can authorize users")
}
}
grantee, err := srv.addressCodec.StringToBytes(msg.Grantee)
if err != nil {
return nil, err
}
if msg.Permissions == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "permissions cannot be nil")
}
// Append the account in the msg to the store's set of authorized super admins
if err = srv.Permissions.Set(ctx, grantee, *msg.Permissions); err != nil {
return nil, err
}
if err = srv.Keeper.EventService.EventManager(ctx).EmitKV(
"authorize_circuit_breaker",
event.NewAttribute("granter", msg.Granter),
event.NewAttribute("grantee", msg.Grantee),
event.NewAttribute("permission", msg.Permissions.String()),
); err != nil {
return nil, err
}
return &types.MsgAuthorizeCircuitBreakerResponse{
Success: true,
}, nil
}
func (srv msgServer) TripCircuitBreaker(ctx context.Context, msg *types.MsgTripCircuitBreaker) (*types.MsgTripCircuitBreakerResponse, error) {
address, err := srv.addressCodec.StringToBytes(msg.Authority)
if err != nil {
return nil, err
}
// Check that the account has the permissions
perms, err := srv.Permissions.Get(ctx, address)
if err != nil && !errorsmod.IsOf(err, collections.ErrNotFound) {
return nil, err
}
for _, msgTypeURL := range msg.MsgTypeUrls {
// check if the message is in the list of allowed messages
isAllowed, err := srv.IsAllowed(ctx, msgTypeURL)
if err != nil {
return nil, err
}
if !isAllowed {
return nil, fmt.Errorf("message %s is already disabled", msgTypeURL)
}
switch {
case perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || bytes.Equal(address, srv.GetAuthority()):
// if the sender is a super admin or the module authority, no need to check perms
case perms.Level == types.Permissions_LEVEL_SOME_MSGS:
// if the sender has permission for some messages, check if the sender has permission for this specific message
if !hasPermissionForMsg(perms, msgTypeURL) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker for message %s", msgTypeURL)
}
default:
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker")
}
if err = srv.DisableList.Set(ctx, msgTypeURL); err != nil {
return nil, err
}
}
urls := strings.Join(msg.GetMsgTypeUrls(), ",")
if err = srv.Keeper.EventService.EventManager(ctx).EmitKV(
"trip_circuit_breaker",
event.NewAttribute("authority", msg.Authority),
event.NewAttribute("msg_url", urls),
); err != nil {
return nil, err
}
return &types.MsgTripCircuitBreakerResponse{
Success: true,
}, nil
}
// ResetCircuitBreaker resumes processing of Msg's in the state machine that
// have been paused using TripCircuitBreaker.
func (srv msgServer) ResetCircuitBreaker(ctx context.Context, msg *types.MsgResetCircuitBreaker) (*types.MsgResetCircuitBreakerResponse, error) {
keeper := srv.Keeper
address, err := srv.addressCodec.StringToBytes(msg.Authority)
if err != nil {
return nil, err
}
// Get the permissions for the account specified in the msg.Authority field
perms, err := keeper.Permissions.Get(ctx, address)
if err != nil && !errorsmod.IsOf(err, collections.ErrNotFound) {
return nil, err
}
for _, msgTypeURL := range msg.MsgTypeUrls {
// check if the message is in the list of allowed messages
isAllowed, err := srv.IsAllowed(ctx, msgTypeURL)
if err != nil {
return nil, err
}
if isAllowed {
return nil, fmt.Errorf("message %s is not disabled", msgTypeURL)
}
switch {
case perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || bytes.Equal(address, srv.GetAuthority()):
// if the sender is a super admin or the module authority, no need to check perms
case perms.Level == types.Permissions_LEVEL_SOME_MSGS:
// if the sender has permission for some messages, check if the sender has permission for this specific message
if !hasPermissionForMsg(perms, msgTypeURL) {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "account does not have permission to reset circuit breaker for message %s", msgTypeURL)
}
default:
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to reset circuit breaker")
}
if err = srv.DisableList.Remove(ctx, msgTypeURL); err != nil {
return nil, err
}
}
urls := strings.Join(msg.GetMsgTypeUrls(), ",")
if err = srv.Keeper.EventService.EventManager(ctx).EmitKV(
"reset_circuit_breaker",
event.NewAttribute("authority", msg.Authority),
event.NewAttribute("msg_url", urls),
); err != nil {
return nil, err
}
return &types.MsgResetCircuitBreakerResponse{Success: true}, nil
}
// hasPermissionForMsg returns true if the account can trip or reset the message.
func hasPermissionForMsg(perms types.Permissions, msg string) bool {
for _, msgurl := range perms.LimitTypeUrls {
if msg == msgurl {
return true
}
}
return false
}