Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom AuthorizationPolicy with code & contract info #1999

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions x/wasm/keeper/authz_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,28 @@ var _ types.AuthorizationPolicy = DefaultAuthorizationPolicy{}

type DefaultAuthorizationPolicy struct{}

func (p DefaultAuthorizationPolicy) CanCreateCode(chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
func (p DefaultAuthorizationPolicy) CanCreateCode(checksum []byte, chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
return chainConfigs.Upload.Allowed(actor) &&
contractConfig.IsSubset(chainConfigs.Instantiate)
}

func (p DefaultAuthorizationPolicy) CanInstantiateContract(config types.AccessConfig, actor sdk.AccAddress) bool {
return config.Allowed(actor)
func (p DefaultAuthorizationPolicy) CanInstantiateContract(i *types.CodeInfo, actor sdk.AccAddress) bool {
return i.InstantiateConfig.Allowed(actor)
}

func (p DefaultAuthorizationPolicy) CanModifyContract(admin, actor sdk.AccAddress) bool {
func (p DefaultAuthorizationPolicy) CanModifyContract(contract *types.ContractInfo, actor sdk.AccAddress) bool {
admin, err := sdk.AccAddressFromBech32(contract.Admin)
if err != nil {
return false
}
return admin != nil && admin.Equals(actor)
}

func (p DefaultAuthorizationPolicy) CanModifyCodeAccessConfig(creator, actor sdk.AccAddress, isSubset bool) bool {
func (p DefaultAuthorizationPolicy) CanModifyCodeAccessConfig(code *types.CodeInfo, actor sdk.AccAddress, isSubset bool) bool {
creator, err := sdk.AccAddressFromBech32(code.Creator)
if err != nil {
return false
}
return creator != nil && creator.Equals(actor) && isSubset
}

Expand Down Expand Up @@ -55,19 +63,19 @@ func newGovAuthorizationPolicy(propagate map[types.AuthorizationPolicyAction]str
}

// CanCreateCode implements AuthorizationPolicy.CanCreateCode to allow gov actions. Always returns true.
func (p GovAuthorizationPolicy) CanCreateCode(types.ChainAccessConfigs, sdk.AccAddress, types.AccessConfig) bool {
func (p GovAuthorizationPolicy) CanCreateCode([]byte, types.ChainAccessConfigs, sdk.AccAddress, types.AccessConfig) bool {
return true
}

func (p GovAuthorizationPolicy) CanInstantiateContract(types.AccessConfig, sdk.AccAddress) bool {
func (p GovAuthorizationPolicy) CanInstantiateContract(*types.CodeInfo, sdk.AccAddress) bool {
return true
}

func (p GovAuthorizationPolicy) CanModifyContract(sdk.AccAddress, sdk.AccAddress) bool {
func (p GovAuthorizationPolicy) CanModifyContract(*types.ContractInfo, sdk.AccAddress) bool {
return true
}

func (p GovAuthorizationPolicy) CanModifyCodeAccessConfig(sdk.AccAddress, sdk.AccAddress, bool) bool {
func (p GovAuthorizationPolicy) CanModifyCodeAccessConfig(*types.CodeInfo, sdk.AccAddress, bool) bool {
return true
}

Expand Down Expand Up @@ -96,26 +104,26 @@ func NewPartialGovAuthorizationPolicy(defaultPolicy types.AuthorizationPolicy, e
return PartialGovAuthorizationPolicy{action: entrypoint, defaultPolicy: defaultPolicy}
}

func (p PartialGovAuthorizationPolicy) CanCreateCode(chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
return p.defaultPolicy.CanCreateCode(chainConfigs, actor, contractConfig)
func (p PartialGovAuthorizationPolicy) CanCreateCode(checksum []byte, chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
return p.defaultPolicy.CanCreateCode(checksum, chainConfigs, actor, contractConfig)
}

func (p PartialGovAuthorizationPolicy) CanInstantiateContract(c types.AccessConfig, actor sdk.AccAddress) bool {
func (p PartialGovAuthorizationPolicy) CanInstantiateContract(c *types.CodeInfo, actor sdk.AccAddress) bool {
if p.action == types.AuthZActionInstantiate {
return true
}
return p.defaultPolicy.CanInstantiateContract(c, actor)
}

func (p PartialGovAuthorizationPolicy) CanModifyContract(admin, actor sdk.AccAddress) bool {
func (p PartialGovAuthorizationPolicy) CanModifyContract(contract *types.ContractInfo, actor sdk.AccAddress) bool {
if p.action == types.AuthZActionMigrateContract {
return true
}
return p.defaultPolicy.CanModifyContract(admin, actor)
return p.defaultPolicy.CanModifyContract(contract, actor)
}

func (p PartialGovAuthorizationPolicy) CanModifyCodeAccessConfig(creator, actor sdk.AccAddress, isSubset bool) bool {
return p.defaultPolicy.CanModifyCodeAccessConfig(creator, actor, isSubset)
func (p PartialGovAuthorizationPolicy) CanModifyCodeAccessConfig(code *types.CodeInfo, actor sdk.AccAddress, isSubset bool) bool {
return p.defaultPolicy.CanModifyCodeAccessConfig(code, actor, isSubset)
}

// SubMessageAuthorizationPolicy always returns self
Expand Down
51 changes: 31 additions & 20 deletions x/wasm/keeper/authz_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ func TestDefaultAuthzPolicyCanCreateCode(t *testing.T) {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
if !spec.panics {
got := policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
got := policy.CanCreateCode([]byte{}, spec.chainConfigs, myActorAddress, spec.contractInstConf)
assert.Equal(t, spec.exp, got)
return
}
assert.Panics(t, func() {
policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
policy.CanCreateCode([]byte{}, spec.chainConfigs, myActorAddress, spec.contractInstConf)
})
})
}
Expand Down Expand Up @@ -104,13 +104,15 @@ func TestDefaultAuthzPolicyCanInstantiateContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
info := types.NewCodeInfo([]byte{}, RandomAccountAddress(t), spec.config)

if !spec.panics {
got := policy.CanInstantiateContract(spec.config, myActorAddress)
got := policy.CanInstantiateContract(&info, myActorAddress)
assert.Equal(t, spec.exp, got)
return
}
assert.Panics(t, func() {
policy.CanInstantiateContract(spec.config, myActorAddress)
policy.CanInstantiateContract(&info, myActorAddress)
})
})
}
Expand Down Expand Up @@ -139,7 +141,8 @@ func TestDefaultAuthzPolicyCanModifyContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
got := policy.CanModifyContract(spec.admin, myActorAddress)
contract := types.NewContractInfo(1, spec.admin, spec.admin, "", nil)
got := policy.CanModifyContract(&contract, myActorAddress)
assert.Equal(t, spec.exp, got)
})
}
Expand Down Expand Up @@ -175,7 +178,8 @@ func TestDefaultAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := DefaultAuthorizationPolicy{}
got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset)
info := types.NewCodeInfo([]byte{}, spec.admin, types.AccessConfig{})
got := policy.CanModifyCodeAccessConfig(&info, myActorAddress, spec.subset)
assert.Equal(t, spec.exp, got)
})
}
Expand Down Expand Up @@ -229,7 +233,7 @@ func TestGovAuthzPolicyCanCreateCode(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanCreateCode(spec.chainConfigs, myActorAddress, spec.contractInstConf)
got := policy.CanCreateCode([]byte{}, spec.chainConfigs, myActorAddress, spec.contractInstConf)
assert.True(t, got)
})
}
Expand Down Expand Up @@ -261,7 +265,8 @@ func TestGovAuthzPolicyCanInstantiateContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanInstantiateContract(spec.config, myActorAddress)
info := types.NewCodeInfo([]byte{}, RandomAccountAddress(t), spec.config)
got := policy.CanInstantiateContract(&info, myActorAddress)
assert.True(t, got)
})
}
Expand All @@ -285,7 +290,8 @@ func TestGovAuthzPolicyCanModifyContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := GovAuthorizationPolicy{}
got := policy.CanModifyContract(spec.admin, myActorAddress)
contract := types.NewContractInfo(1, spec.admin, spec.admin, "", nil)
got := policy.CanModifyContract(&contract, myActorAddress)
assert.True(t, got)
})
}
Expand Down Expand Up @@ -315,7 +321,8 @@ func TestGovAuthzPolicyCanModifyCodeAccessConfig(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := newGovAuthorizationPolicy(nil)
got := policy.CanModifyCodeAccessConfig(spec.admin, myActorAddress, spec.subset)
info := types.NewCodeInfo([]byte{}, spec.admin, types.AccessConfig{})
got := policy.CanModifyCodeAccessConfig(&info, myActorAddress, spec.subset)
assert.True(t, got)
})
}
Expand Down Expand Up @@ -373,7 +380,8 @@ func TestPartialGovAuthorizationPolicyCanInstantiateContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := NewPartialGovAuthorizationPolicy(AlwaysRejectTestAuthZPolicy{}, spec.allowedAction)
got := policy.CanInstantiateContract(types.AccessConfig{}, nil)
info := types.NewCodeInfo([]byte{}, RandomAccountAddress(t), types.AccessConfig{})
got := policy.CanInstantiateContract(&info, nil)
assert.Equal(t, spec.exp, got)
})
}
Expand All @@ -399,22 +407,25 @@ func TestPartialGovAuthorizationPolicyCanModifyContract(t *testing.T) {
for name, spec := range specs {
t.Run(name, func(t *testing.T) {
policy := NewPartialGovAuthorizationPolicy(AlwaysRejectTestAuthZPolicy{}, spec.allowedAction)
got := policy.CanModifyContract(nil, nil)
contract := types.NewContractInfo(1, nil, nil, "", nil)
got := policy.CanModifyContract(&contract, nil)
assert.Equal(t, spec.exp, got)
})
}
}

func TestPartialGovAuthorizationPolicyDelegatedOnly(t *testing.T) {
info := types.NewCodeInfo([]byte{}, RandomAccountAddress(t), types.AccessConfig{})

for _, v := range []types.AuthorizationPolicy{AlwaysRejectTestAuthZPolicy{}, NewGovAuthorizationPolicy()} {
policy := NewPartialGovAuthorizationPolicy(v, types.AuthZActionInstantiate)

got := policy.CanCreateCode(types.ChainAccessConfigs{}, nil, types.AccessConfig{})
exp := v.CanCreateCode(types.ChainAccessConfigs{}, nil, types.AccessConfig{})
got := policy.CanCreateCode([]byte{}, types.ChainAccessConfigs{}, nil, types.AccessConfig{})
exp := v.CanCreateCode([]byte{}, types.ChainAccessConfigs{}, nil, types.AccessConfig{})
assert.Equal(t, exp, got)

got = policy.CanModifyCodeAccessConfig(nil, nil, false)
exp = v.CanModifyCodeAccessConfig(nil, nil, false)
got = policy.CanModifyCodeAccessConfig(&info, nil, false)
exp = v.CanModifyCodeAccessConfig(&info, nil, false)
assert.Equal(t, exp, got)
}
}
Expand All @@ -431,19 +442,19 @@ var _ types.AuthorizationPolicy = AlwaysRejectTestAuthZPolicy{}

type AlwaysRejectTestAuthZPolicy struct{}

func (a AlwaysRejectTestAuthZPolicy) CanCreateCode(chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
func (a AlwaysRejectTestAuthZPolicy) CanCreateCode(checksum []byte, chainConfigs types.ChainAccessConfigs, actor sdk.AccAddress, contractConfig types.AccessConfig) bool {
return false
}

func (a AlwaysRejectTestAuthZPolicy) CanInstantiateContract(c types.AccessConfig, actor sdk.AccAddress) bool {
func (a AlwaysRejectTestAuthZPolicy) CanInstantiateContract(code *types.CodeInfo, actor sdk.AccAddress) bool {
return false
}

func (a AlwaysRejectTestAuthZPolicy) CanModifyContract(admin, actor sdk.AccAddress) bool {
func (a AlwaysRejectTestAuthZPolicy) CanModifyContract(contract *types.ContractInfo, actor sdk.AccAddress) bool {
return false
}

func (a AlwaysRejectTestAuthZPolicy) CanModifyCodeAccessConfig(creator, actor sdk.AccAddress, isSubset bool) bool {
func (a AlwaysRejectTestAuthZPolicy) CanModifyCodeAccessConfig(code *types.CodeInfo, actor sdk.AccAddress, isSubset bool) bool {
return false
}

Expand Down
28 changes: 16 additions & 12 deletions x/wasm/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ type Keeper struct {
// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string

customAuthPolicy func(ctx context.Context, actor string) (types.AuthorizationPolicy, bool)
}

func (k Keeper) getUploadAccessConfig(ctx context.Context) types.AccessConfig {
Expand Down Expand Up @@ -157,10 +159,6 @@ func (k Keeper) create(ctx context.Context, creator sdk.AccAddress, wasmCode []b
Upload: k.getUploadAccessConfig(sdkCtx),
}

if !authZ.CanCreateCode(chainConfigs, creator, *instantiateAccess) {
return 0, checksum, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
}

if ioutils.IsGzip(wasmCode) {
sdkCtx.GasMeter().ConsumeGas(k.gasRegister.UncompressCosts(len(wasmCode)), "Uncompress gzip bytecode")
wasmCode, err = ioutils.Uncompress(wasmCode, int64(types.MaxWasmSize))
Expand All @@ -171,6 +169,11 @@ func (k Keeper) create(ctx context.Context, creator sdk.AccAddress, wasmCode []b

gasLeft := k.runtimeGasForContract(sdkCtx)
checksum, gasUsed, err := k.wasmVM.StoreCode(wasmCode, gasLeft)

if !authZ.CanCreateCode(checksum, chainConfigs, creator, *instantiateAccess) {
return 0, checksum, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
}

k.consumeRuntimeGas(sdkCtx, gasUsed)
if err != nil {
return 0, checksum, errorsmod.Wrap(types.ErrCreateFailed, err.Error())
Expand Down Expand Up @@ -258,7 +261,7 @@ func (k Keeper) instantiate(
if codeInfo == nil {
return nil, nil, types.ErrNoSuchCodeFn(codeID).Wrapf("code id %d", codeID)
}
if !authPolicy.CanInstantiateContract(codeInfo.InstantiateConfig, creator) {
if !authPolicy.CanInstantiateContract(codeInfo, creator) {
return nil, nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not instantiate")
}
contractAddress := addressGenerator(ctx, codeID, codeInfo.CodeHash)
Expand Down Expand Up @@ -452,16 +455,17 @@ func (k Keeper) migrate(
if contractInfo == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract")
}
if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not migrate")
}

newCodeInfo := k.GetCodeInfo(ctx, newCodeID)
if newCodeInfo == nil {
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unknown code")
}

if !authZ.CanInstantiateContract(newCodeInfo.InstantiateConfig, caller) {
if !authZ.CanModifyContract(contractInfo, caller) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not migrate")
}

if !authZ.CanInstantiateContract(newCodeInfo, caller) {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "to use new code")
}

Expand Down Expand Up @@ -723,7 +727,7 @@ func (k Keeper) setContractAdmin(ctx context.Context, contractAddress, caller, n
if contractInfo == nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract")
}
if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) {
if !authZ.CanModifyContract(contractInfo, caller) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not modify contract")
}
newAdminStr := newAdmin.String()
Expand All @@ -744,7 +748,7 @@ func (k Keeper) setContractLabel(ctx context.Context, contractAddress, caller sd
if contractInfo == nil {
return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "unknown contract")
}
if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) {
if !authZ.CanModifyContract(contractInfo, caller) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not modify contract")
}
contractInfo.Label = newLabel
Expand Down Expand Up @@ -1178,7 +1182,7 @@ func (k Keeper) setAccessConfig(ctx context.Context, codeID uint64, caller sdk.A
return types.ErrNoSuchCodeFn(codeID).Wrapf("code id %d", codeID)
}
isSubset := newConfig.Permission.IsSubset(k.getInstantiateAccessConfig(ctx))
if !authz.CanModifyCodeAccessConfig(sdk.MustAccAddressFromBech32(info.Creator), caller, isSubset) {
if !authz.CanModifyCodeAccessConfig(info, caller, isSubset) {
return errorsmod.Wrap(sdkerrors.ErrUnauthorized, "can not modify code access config")
}

Expand Down
5 changes: 5 additions & 0 deletions x/wasm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,11 @@ func contains[T comparable](src []T, o T) bool {
}

func (m msgServer) selectAuthorizationPolicy(ctx context.Context, actor string) types.AuthorizationPolicy {
if m.keeper.customAuthPolicy != nil {
if policy, ok := m.keeper.customAuthPolicy(ctx, actor); ok {
return policy
}
}
if actor == m.keeper.GetAuthority() {
return newGovAuthorizationPolicy(m.keeper.propagateGovAuthorization)
}
Expand Down
Loading