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