2021-01-25 08:41:30 -08:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
|
|
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
|
|
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
2021-02-19 22:57:57 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/authz/exported"
|
2021-01-25 08:41:30 -08:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/authz/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Keeper struct {
|
|
|
|
storeKey sdk.StoreKey
|
|
|
|
cdc codec.BinaryMarshaler
|
|
|
|
router *baseapp.MsgServiceRouter
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewKeeper constructs a message authorization Keeper
|
|
|
|
func NewKeeper(storeKey sdk.StoreKey, cdc codec.BinaryMarshaler, router *baseapp.MsgServiceRouter) Keeper {
|
|
|
|
return Keeper{
|
|
|
|
storeKey: storeKey,
|
|
|
|
cdc: cdc,
|
|
|
|
router: router,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logger returns a module-specific logger.
|
|
|
|
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
|
|
|
|
return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
|
|
|
|
}
|
|
|
|
|
|
|
|
// getAuthorizationGrant returns grant between granter and grantee for the given msg type
|
|
|
|
func (k Keeper) getAuthorizationGrant(ctx sdk.Context, grantStoreKey []byte) (grant types.AuthorizationGrant, found bool) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
bz := store.Get(grantStoreKey)
|
|
|
|
if bz == nil {
|
|
|
|
return grant, false
|
|
|
|
}
|
|
|
|
k.cdc.MustUnmarshalBinaryBare(bz, &grant)
|
|
|
|
return grant, true
|
|
|
|
}
|
|
|
|
|
2021-02-19 22:57:57 -08:00
|
|
|
func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, updated exported.Authorization) error {
|
2021-01-25 08:41:30 -08:00
|
|
|
grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, updated.MethodName())
|
|
|
|
grant, found := k.getAuthorizationGrant(ctx, grantStoreKey)
|
|
|
|
if !found {
|
|
|
|
return sdkerrors.Wrapf(sdkerrors.ErrNotFound, "authorization not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
msg, ok := updated.(proto.Message)
|
|
|
|
if !ok {
|
|
|
|
sdkerrors.Wrapf(sdkerrors.ErrPackAny, "cannot proto marshal %T", updated)
|
|
|
|
}
|
|
|
|
|
|
|
|
any, err := codectypes.NewAnyWithValue(msg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
grant.Authorization = any
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
store.Set(grantStoreKey, k.cdc.MustMarshalBinaryBare(&grant))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DispatchActions attempts to execute the provided messages via authorization
|
|
|
|
// grants from the message signer to the grantee.
|
|
|
|
func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, serviceMsgs []sdk.ServiceMsg) (*sdk.Result, error) {
|
|
|
|
var msgResult *sdk.Result
|
|
|
|
var err error
|
|
|
|
for _, serviceMsg := range serviceMsgs {
|
|
|
|
signers := serviceMsg.GetSigners()
|
|
|
|
if len(signers) != 1 {
|
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "authorization can be given to msg with only one signer")
|
|
|
|
}
|
|
|
|
granter := signers[0]
|
|
|
|
if !granter.Equals(grantee) {
|
|
|
|
authorization, _ := k.GetOrRevokeAuthorization(ctx, grantee, granter, serviceMsg.MethodName)
|
|
|
|
if authorization == nil {
|
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "authorization not found")
|
|
|
|
}
|
2021-02-19 22:57:57 -08:00
|
|
|
updated, del, err := authorization.Accept(serviceMsg, ctx.BlockHeader())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2021-01-25 08:41:30 -08:00
|
|
|
}
|
|
|
|
if del {
|
|
|
|
k.Revoke(ctx, grantee, granter, serviceMsg.Type())
|
|
|
|
} else if updated != nil {
|
|
|
|
err = k.update(ctx, grantee, granter, updated)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
handler := k.router.Handler(serviceMsg.Route())
|
|
|
|
|
|
|
|
if handler == nil {
|
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized message route: %s", serviceMsg.Route())
|
|
|
|
}
|
|
|
|
|
|
|
|
msgResult, err = handler(ctx, serviceMsg.Request)
|
|
|
|
if err != nil {
|
|
|
|
return nil, sdkerrors.Wrapf(err, "failed to execute message; message %s", serviceMsg.MethodName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return msgResult, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grant method grants the provided authorization to the grantee on the granter's account with the provided expiration
|
|
|
|
// time. If there is an existing authorization grant for the same `sdk.Msg` type, this grant
|
|
|
|
// overwrites that.
|
2021-02-19 22:57:57 -08:00
|
|
|
func (k Keeper) Grant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization exported.Authorization, expiration time.Time) error {
|
2021-01-25 08:41:30 -08:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
|
|
|
|
grant, err := types.NewAuthorizationGrant(authorization, expiration)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bz := k.cdc.MustMarshalBinaryBare(&grant)
|
|
|
|
grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, authorization.MethodName())
|
|
|
|
store.Set(grantStoreKey, bz)
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventGrantAuthorization,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGrantType, authorization.MethodName()),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGranterAddress, granter.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGranteeAddress, grantee.String()),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revoke method revokes any authorization for the provided message type granted to the grantee by the granter.
|
|
|
|
func (k Keeper) Revoke(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) error {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
grantStoreKey := types.GetAuthorizationStoreKey(grantee, granter, msgType)
|
|
|
|
_, found := k.getAuthorizationGrant(ctx, grantStoreKey)
|
|
|
|
if !found {
|
|
|
|
return sdkerrors.Wrap(sdkerrors.ErrNotFound, "authorization not found")
|
|
|
|
}
|
|
|
|
store.Delete(grantStoreKey)
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventRevokeAuthorization,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGrantType, msgType),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGranterAddress, granter.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyGranteeAddress, grantee.String()),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAuthorizations Returns list of `Authorizations` granted to the grantee by the granter.
|
2021-02-19 22:57:57 -08:00
|
|
|
func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress) (authorizations []exported.Authorization) {
|
2021-01-25 08:41:30 -08:00
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
key := types.GetAuthorizationStoreKey(grantee, granter, "")
|
|
|
|
iter := sdk.KVStorePrefixIterator(store, key)
|
|
|
|
defer iter.Close()
|
|
|
|
var authorization types.AuthorizationGrant
|
|
|
|
for ; iter.Valid(); iter.Next() {
|
|
|
|
k.cdc.MustUnmarshalBinaryBare(iter.Value(), &authorization)
|
|
|
|
authorizations = append(authorizations, authorization.GetAuthorizationGrant())
|
|
|
|
}
|
|
|
|
return authorizations
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetOrRevokeAuthorization Returns any `Authorization` (or `nil`), with the expiration time,
|
|
|
|
// granted to the grantee by the granter for the provided msg type.
|
|
|
|
// If the Authorization is expired already, it will revoke the authorization and return nil
|
2021-02-19 22:57:57 -08:00
|
|
|
func (k Keeper) GetOrRevokeAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap exported.Authorization, expiration time.Time) {
|
2021-01-25 08:41:30 -08:00
|
|
|
grant, found := k.getAuthorizationGrant(ctx, types.GetAuthorizationStoreKey(grantee, granter, msgType))
|
|
|
|
if !found {
|
|
|
|
return nil, time.Time{}
|
|
|
|
}
|
|
|
|
if grant.Expiration.Before(ctx.BlockHeader().Time) {
|
|
|
|
k.Revoke(ctx, grantee, granter, msgType)
|
|
|
|
return nil, time.Time{}
|
|
|
|
}
|
|
|
|
|
|
|
|
return grant.GetAuthorizationGrant(), grant.Expiration
|
|
|
|
}
|
|
|
|
|
|
|
|
// IterateGrants iterates over all authorization grants
|
|
|
|
func (k Keeper) IterateGrants(ctx sdk.Context,
|
|
|
|
handler func(granterAddr sdk.AccAddress, granteeAddr sdk.AccAddress, grant types.AuthorizationGrant) bool) {
|
|
|
|
store := ctx.KVStore(k.storeKey)
|
|
|
|
iter := sdk.KVStorePrefixIterator(store, types.GrantKey)
|
|
|
|
defer iter.Close()
|
|
|
|
for ; iter.Valid(); iter.Next() {
|
|
|
|
var grant types.AuthorizationGrant
|
|
|
|
granterAddr, granteeAddr := types.ExtractAddressesFromGrantKey(iter.Key())
|
|
|
|
k.cdc.MustUnmarshalBinaryBare(iter.Value(), &grant)
|
|
|
|
if handler(granterAddr, granteeAddr, grant) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|