package manifest import ( "encoding/hex" "encoding/json" "errors" "fmt" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" ) // PermissionType represents permission type. type PermissionType uint8 const ( // PermissionWildcard allows everything. PermissionWildcard PermissionType = 0 // PermissionHash restricts called contracts based on hash. PermissionHash PermissionType = 1 // PermissionGroup restricts called contracts based on public key. PermissionGroup PermissionType = 2 ) // PermissionDesc is a permission descriptor. type PermissionDesc struct { Type PermissionType Value interface{} } // Permission describes which contracts may be invoked and which methods are called. type Permission struct { Contract PermissionDesc `json:"contract"` Methods WildStrings `json:"methods"` } type permissionAux struct { Contract PermissionDesc `json:"contract"` Methods WildStrings `json:"methods"` } // NewPermission returns new permission of a given type. func NewPermission(typ PermissionType, args ...interface{}) *Permission { return &Permission{ Contract: *newPermissionDesc(typ, args...), } } func newPermissionDesc(typ PermissionType, args ...interface{}) *PermissionDesc { desc := &PermissionDesc{Type: typ} switch typ { case PermissionWildcard: if len(args) != 0 { panic("wildcard permission has no arguments") } case PermissionHash: if len(args) == 0 { panic("hash permission should have an argument") } else if u, ok := args[0].(util.Uint160); !ok { panic("hash permission should have util.Uint160 argument") } else { desc.Value = u } case PermissionGroup: if len(args) == 0 { panic("group permission should have an argument") } else if pub, ok := args[0].(*keys.PublicKey); !ok { panic("group permission should have a public key argument") } else { desc.Value = pub } } return desc } // Hash returns hash for hash-permission. func (d *PermissionDesc) Hash() util.Uint160 { return d.Value.(util.Uint160) } // Group returns group's public key for group-permission. func (d *PermissionDesc) Group() *keys.PublicKey { return d.Value.(*keys.PublicKey) } // IsAllowed checks if method is allowed to be executed. func (p *Permission) IsAllowed(m *Manifest, method string) bool { switch p.Contract.Type { case PermissionWildcard: return true case PermissionHash: if !p.Contract.Hash().Equals(m.ABI.Hash) { return false } case PermissionGroup: g := p.Contract.Group() for i := range m.Groups { if !g.Equal(m.Groups[i].PublicKey) { return false } } default: panic(fmt.Sprintf("unexpected permission: %d", p.Contract.Type)) } if p.Methods.IsWildcard() { return true } return p.Methods.Contains(method) } // UnmarshalJSON implements json.Unmarshaler interface. func (p *Permission) UnmarshalJSON(data []byte) error { aux := new(permissionAux) if err := json.Unmarshal(data, aux); err != nil { return err } p.Contract = aux.Contract p.Methods = aux.Methods return nil } // MarshalJSON implements json.Marshaler interface. func (d *PermissionDesc) MarshalJSON() ([]byte, error) { switch d.Type { case PermissionHash: return json.Marshal("0x" + d.Hash().StringLE()) case PermissionGroup: return json.Marshal(hex.EncodeToString(d.Group().Bytes())) default: return []byte(`"*"`), nil } } // UnmarshalJSON implements json.Unmarshaler interface. func (d *PermissionDesc) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return err } const uint160HexSize = 2 * util.Uint160Size switch len(s) { case 2 + uint160HexSize: // allow to unmarshal both hex and 0xhex forms if s[0] != '0' || s[1] != 'x' { return errors.New("invalid uint160") } s = s[2:] fallthrough case uint160HexSize: u, err := util.Uint160DecodeStringLE(s) if err != nil { return err } d.Type = PermissionHash d.Value = u return nil case 66: pub, err := keys.NewPublicKeyFromString(s) if err != nil { return err } d.Type = PermissionGroup d.Value = pub return nil case 1: if s == "*" { d.Type = PermissionWildcard return nil } } return errors.New("unknown permission") }