github.com/Finschia/finschia-sdk@v0.48.1/x/authz/keeper/keeper.go (about) 1 package keeper 2 3 import ( 4 "fmt" 5 "strconv" 6 "time" 7 8 "github.com/gogo/protobuf/proto" 9 10 "github.com/Finschia/ostracon/libs/log" 11 abci "github.com/tendermint/tendermint/abci/types" 12 13 "github.com/Finschia/finschia-sdk/baseapp" 14 "github.com/Finschia/finschia-sdk/codec" 15 codectypes "github.com/Finschia/finschia-sdk/codec/types" 16 sdk "github.com/Finschia/finschia-sdk/types" 17 sdkerrors "github.com/Finschia/finschia-sdk/types/errors" 18 "github.com/Finschia/finschia-sdk/x/authz" 19 ) 20 21 type Keeper struct { 22 storeKey sdk.StoreKey 23 cdc codec.BinaryCodec 24 router *baseapp.MsgServiceRouter 25 } 26 27 // NewKeeper constructs a message authorization Keeper 28 func NewKeeper(storeKey sdk.StoreKey, cdc codec.BinaryCodec, router *baseapp.MsgServiceRouter) Keeper { 29 return Keeper{ 30 storeKey: storeKey, 31 cdc: cdc, 32 router: router, 33 } 34 } 35 36 // Logger returns a module-specific logger. 37 func (k Keeper) Logger(ctx sdk.Context) log.Logger { 38 return ctx.Logger().With("module", fmt.Sprintf("x/%s", authz.ModuleName)) 39 } 40 41 // getGrant returns grant stored at skey. 42 func (k Keeper) getGrant(ctx sdk.Context, skey []byte) (grant authz.Grant, found bool) { 43 store := ctx.KVStore(k.storeKey) 44 bz := store.Get(skey) 45 if bz == nil { 46 return grant, false 47 } 48 k.cdc.MustUnmarshal(bz, &grant) 49 return grant, true 50 } 51 52 func (k Keeper) update(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, updated authz.Authorization) error { 53 skey := grantStoreKey(grantee, granter, updated.MsgTypeURL()) 54 grant, found := k.getGrant(ctx, skey) 55 if !found { 56 return sdkerrors.ErrNotFound.Wrap("authorization not found") 57 } 58 59 msg, ok := updated.(proto.Message) 60 if !ok { 61 sdkerrors.ErrPackAny.Wrapf("cannot proto marshal %T", updated) 62 } 63 64 any, err := codectypes.NewAnyWithValue(msg) 65 if err != nil { 66 return err 67 } 68 69 grant.Authorization = any 70 store := ctx.KVStore(k.storeKey) 71 store.Set(skey, k.cdc.MustMarshal(&grant)) 72 return nil 73 } 74 75 // DispatchActions attempts to execute the provided messages via authorization 76 // grants from the message signer to the grantee. 77 func (k Keeper) DispatchActions(ctx sdk.Context, grantee sdk.AccAddress, msgs []sdk.Msg) ([][]byte, error) { 78 results := make([][]byte, len(msgs)) 79 80 for i, msg := range msgs { 81 signers := msg.GetSigners() 82 if len(signers) != 1 { 83 return nil, sdkerrors.ErrInvalidRequest.Wrap("authorization can be given to msg with only one signer") 84 } 85 86 granter := signers[0] 87 88 // If granter != grantee then check authorization.Accept, otherwise we 89 // implicitly accept. 90 if !granter.Equals(grantee) { 91 authorization, _ := k.GetCleanAuthorization(ctx, grantee, granter, sdk.MsgTypeURL(msg)) 92 if authorization == nil { 93 return nil, sdkerrors.ErrUnauthorized.Wrap("authorization not found") 94 } 95 resp, err := authorization.Accept(ctx, msg) 96 if err != nil { 97 return nil, err 98 } 99 100 if resp.Delete { 101 err = k.DeleteGrant(ctx, grantee, granter, sdk.MsgTypeURL(msg)) 102 } else if resp.Updated != nil { 103 err = k.update(ctx, grantee, granter, resp.Updated) 104 } 105 if err != nil { 106 return nil, err 107 } 108 109 if !resp.Accept { 110 return nil, sdkerrors.ErrUnauthorized 111 } 112 } 113 114 handler := k.router.Handler(msg) 115 if handler == nil { 116 return nil, sdkerrors.ErrUnknownRequest.Wrapf("unrecognized message route: %s", sdk.MsgTypeURL(msg)) 117 } 118 119 msgResp, err := handler(ctx, msg) 120 if err != nil { 121 return nil, sdkerrors.Wrapf(err, "failed to execute message; message %v", msg) 122 } 123 124 results[i] = msgResp.Data 125 126 // emit the events from the dispatched actions 127 events := msgResp.Events 128 sdkEvents := make([]sdk.Event, 0, len(events)) 129 for _, event := range events { 130 e := event 131 e.Attributes = append(e.Attributes, abci.EventAttribute{Key: []byte("authz_msg_index"), Value: []byte(strconv.Itoa(i))}) 132 133 sdkEvents = append(sdkEvents, sdk.Event(e)) 134 } 135 136 ctx.EventManager().EmitEvents(sdkEvents) 137 } 138 139 return results, nil 140 } 141 142 // SaveGrant method grants the provided authorization to the grantee on the granter's account 143 // with the provided expiration time. If there is an existing authorization grant for the 144 // same `sdk.Msg` type, this grant overwrites that. 145 func (k Keeper) SaveGrant(ctx sdk.Context, grantee, granter sdk.AccAddress, authorization authz.Authorization, expiration time.Time) error { 146 store := ctx.KVStore(k.storeKey) 147 148 grant, err := authz.NewGrant(ctx.BlockTime(), authorization, expiration) 149 if err != nil { 150 return err 151 } 152 153 bz := k.cdc.MustMarshal(&grant) 154 skey := grantStoreKey(grantee, granter, authorization.MsgTypeURL()) 155 store.Set(skey, bz) 156 return ctx.EventManager().EmitTypedEvent(&authz.EventGrant{ 157 MsgTypeUrl: authorization.MsgTypeURL(), 158 Granter: granter.String(), 159 Grantee: grantee.String(), 160 }) 161 } 162 163 // DeleteGrant revokes any authorization for the provided message type granted to the grantee 164 // by the granter. 165 func (k Keeper) DeleteGrant(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) error { 166 store := ctx.KVStore(k.storeKey) 167 skey := grantStoreKey(grantee, granter, msgType) 168 _, found := k.getGrant(ctx, skey) 169 if !found { 170 return sdkerrors.ErrNotFound.Wrap("authorization not found") 171 } 172 store.Delete(skey) 173 return ctx.EventManager().EmitTypedEvent(&authz.EventRevoke{ 174 MsgTypeUrl: msgType, 175 Granter: granter.String(), 176 Grantee: grantee.String(), 177 }) 178 } 179 180 // GetAuthorizations Returns list of `Authorizations` granted to the grantee by the granter. 181 func (k Keeper) GetAuthorizations(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress) (authorizations []authz.Authorization) { 182 store := ctx.KVStore(k.storeKey) 183 key := grantStoreKey(grantee, granter, "") 184 iter := sdk.KVStorePrefixIterator(store, key) 185 defer iter.Close() 186 var authorization authz.Grant 187 for ; iter.Valid(); iter.Next() { 188 k.cdc.MustUnmarshal(iter.Value(), &authorization) 189 authorizations = append(authorizations, authorization.GetAuthorization()) 190 } 191 return authorizations 192 } 193 194 // GetCleanAuthorization returns an `Authorization` and it's expiration time for 195 // (grantee, granter, message name) grant. If there is no grant `nil` is returned. 196 // If the grant is expired, the grant is revoked, removed from the storage, and `nil` is returned. 197 func (k Keeper) GetCleanAuthorization(ctx sdk.Context, grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) (cap authz.Authorization, expiration time.Time) { 198 grant, found := k.getGrant(ctx, grantStoreKey(grantee, granter, msgType)) 199 if !found { 200 return nil, time.Time{} 201 } 202 if grant.Expiration.Before(ctx.BlockHeader().Time) { 203 k.DeleteGrant(ctx, grantee, granter, msgType) 204 return nil, time.Time{} 205 } 206 207 return grant.GetAuthorization(), grant.Expiration 208 } 209 210 // IterateGrants iterates over all authorization grants 211 // This function should be used with caution because it can involve significant IO operations. 212 // It should not be used in query or msg services without charging additional gas. 213 func (k Keeper) IterateGrants(ctx sdk.Context, 214 handler func(granterAddr sdk.AccAddress, granteeAddr sdk.AccAddress, grant authz.Grant) bool, 215 ) { 216 store := ctx.KVStore(k.storeKey) 217 iter := sdk.KVStorePrefixIterator(store, GrantKey) 218 defer iter.Close() 219 for ; iter.Valid(); iter.Next() { 220 var grant authz.Grant 221 granterAddr, granteeAddr := addressesFromGrantStoreKey(iter.Key()) 222 k.cdc.MustUnmarshal(iter.Value(), &grant) 223 if handler(granterAddr, granteeAddr, grant) { 224 break 225 } 226 } 227 } 228 229 // ExportGenesis returns a GenesisState for a given context. 230 func (k Keeper) ExportGenesis(ctx sdk.Context) *authz.GenesisState { 231 var entries []authz.GrantAuthorization 232 k.IterateGrants(ctx, func(granter, grantee sdk.AccAddress, grant authz.Grant) bool { 233 exp := grant.Expiration 234 entries = append(entries, authz.GrantAuthorization{ 235 Granter: granter.String(), 236 Grantee: grantee.String(), 237 Expiration: exp, 238 Authorization: grant.Authorization, 239 }) 240 return false 241 }) 242 243 return authz.NewGenesisState(entries) 244 } 245 246 // InitGenesis new authz genesis 247 func (k Keeper) InitGenesis(ctx sdk.Context, data *authz.GenesisState) { 248 for _, entry := range data.Authorization { 249 if entry.Expiration.Before(ctx.BlockTime()) { 250 continue 251 } 252 253 grantee := sdk.MustAccAddressFromBech32(entry.Grantee) 254 granter := sdk.MustAccAddressFromBech32(entry.Granter) 255 a, ok := entry.Authorization.GetCachedValue().(authz.Authorization) 256 if !ok { 257 panic("expected authorization") 258 } 259 260 err := k.SaveGrant(ctx, grantee, granter, a, entry.Expiration) 261 if err != nil { 262 panic(err) 263 } 264 } 265 }