package types
|
|
|
|
import (
|
|
"fmt"
|
|
. "github.com/tendermint/tendermint/common"
|
|
"reflect"
|
|
)
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
|
|
var (
|
|
GlobalPermissionsAddress = Zero256[:20]
|
|
GlobalPermissionsAddress256 = Zero256
|
|
DougAddress = append([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, []byte("THISISDOUG")...)
|
|
DougAddress256 = LeftPadWord256(DougAddress)
|
|
)
|
|
|
|
// A particular permission
|
|
type PermFlag uint64
|
|
|
|
// Base permission references are like unix (the index is already bit shifted)
|
|
const (
|
|
Root PermFlag = 1 << iota // 1
|
|
Send // 2
|
|
Call // 4
|
|
CreateContract // 8
|
|
CreateAccount // 16
|
|
Bond // 32
|
|
Name // 64
|
|
|
|
DefaultBBPB = Send | Call | CreateContract | CreateAccount | Bond | Name
|
|
|
|
// XXX: must be adjusted if base perms added/removed
|
|
NumBasePermissions uint = 7
|
|
TopBasePermission PermFlag = 1 << (NumBasePermissions - 1)
|
|
AllBasePermissions PermFlag = TopBasePermission | (TopBasePermission - 1)
|
|
|
|
AllSet PermFlag = AllBasePermissions | AllSNativePermissions
|
|
)
|
|
|
|
// should have same ordering as above
|
|
type BasePermissionsString struct {
|
|
Root bool `json:"root,omitempty"`
|
|
Send bool `json:"send,omitempty"`
|
|
Call bool `json:"call,omitempty"`
|
|
CreateContract bool `json:"create_contract,omitempty"`
|
|
CreateAccount bool `json:"create_account,omitempty"`
|
|
Bond bool `json:"bond,omitempty"`
|
|
Name bool `json:"name,omitempty"`
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
// Base chain permissions struct
|
|
type BasePermissions struct {
|
|
// bit array with "has"/"doesn't have" for each permission
|
|
Perms PermFlag `json:"perms"`
|
|
|
|
// bit array with "set"/"not set" for each permission (not-set should fall back to global)
|
|
SetBit PermFlag `json:"set"`
|
|
}
|
|
|
|
func NewBasePermissions() *BasePermissions {
|
|
return &BasePermissions{0, 0}
|
|
}
|
|
|
|
// Get a permission value. ty should be a power of 2.
|
|
// ErrValueNotSet is returned if the permission's set bit is off,
|
|
// and should be caught by caller so the global permission can be fetched
|
|
func (p *BasePermissions) Get(ty PermFlag) (bool, error) {
|
|
if ty == 0 {
|
|
return false, ErrInvalidPermission(ty)
|
|
}
|
|
if p.SetBit&ty == 0 {
|
|
return false, ErrValueNotSet(ty)
|
|
}
|
|
return p.Perms&ty > 0, nil
|
|
}
|
|
|
|
// Set a permission bit. Will set the permission's set bit to true.
|
|
func (p *BasePermissions) Set(ty PermFlag, value bool) error {
|
|
if ty == 0 {
|
|
return ErrInvalidPermission(ty)
|
|
}
|
|
p.SetBit |= ty
|
|
if value {
|
|
p.Perms |= ty
|
|
} else {
|
|
p.Perms &= ^ty
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Set the permission's set bit to false
|
|
func (p *BasePermissions) Unset(ty PermFlag) error {
|
|
if ty == 0 {
|
|
return ErrInvalidPermission(ty)
|
|
}
|
|
p.SetBit &= ^ty
|
|
return nil
|
|
}
|
|
|
|
// Check if the permission is set
|
|
func (p *BasePermissions) IsSet(ty PermFlag) bool {
|
|
if ty == 0 {
|
|
return false
|
|
}
|
|
return p.SetBit&ty > 0
|
|
}
|
|
|
|
func (p *BasePermissions) Copy() *BasePermissions {
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return &BasePermissions{
|
|
Perms: p.Perms,
|
|
SetBit: p.SetBit,
|
|
}
|
|
}
|
|
|
|
func (p *BasePermissions) String() string {
|
|
return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit)
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
|
|
type AccountPermissions struct {
|
|
Base *BasePermissions `json:"base"`
|
|
Roles []string `json:"roles"`
|
|
}
|
|
|
|
func NewAccountPermissions() *AccountPermissions {
|
|
return &AccountPermissions{
|
|
Base: NewBasePermissions(),
|
|
Roles: []string{},
|
|
}
|
|
}
|
|
|
|
// Returns true if the role is found
|
|
func (aP *AccountPermissions) HasRole(role string) bool {
|
|
role = string(LeftPadBytes([]byte(role), 32))
|
|
for _, r := range aP.Roles {
|
|
if r == role {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Returns true if the role is added, and false if it already exists
|
|
func (aP *AccountPermissions) AddRole(role string) bool {
|
|
role = string(LeftPadBytes([]byte(role), 32))
|
|
for _, r := range aP.Roles {
|
|
if r == role {
|
|
return false
|
|
}
|
|
}
|
|
aP.Roles = append(aP.Roles, role)
|
|
return true
|
|
}
|
|
|
|
// Returns true if the role is removed, and false if it is not found
|
|
func (aP *AccountPermissions) RmRole(role string) bool {
|
|
role = string(LeftPadBytes([]byte(role), 32))
|
|
for i, r := range aP.Roles {
|
|
if r == role {
|
|
post := []string{}
|
|
if len(aP.Roles) > i+1 {
|
|
post = aP.Roles[i+1:]
|
|
}
|
|
aP.Roles = append(aP.Roles[:i], post...)
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (aP *AccountPermissions) Copy() *AccountPermissions {
|
|
if aP == nil {
|
|
return nil
|
|
}
|
|
r := make([]string, len(aP.Roles))
|
|
copy(r, aP.Roles)
|
|
return &AccountPermissions{
|
|
Base: aP.Base.Copy(),
|
|
Roles: r,
|
|
}
|
|
}
|
|
|
|
func NewDefaultAccountPermissions() *AccountPermissions {
|
|
return &AccountPermissions{
|
|
Base: &BasePermissions{
|
|
Perms: DefaultBBPB,
|
|
SetBit: AllSet,
|
|
},
|
|
Roles: []string{},
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
// Utilities to make bitmasks human readable
|
|
|
|
func NewDefaultAccountPermissionsString() BasePermissionsString {
|
|
return BasePermissionsString{
|
|
Root: false,
|
|
Bond: true,
|
|
Send: true,
|
|
Call: true,
|
|
Name: true,
|
|
CreateAccount: true,
|
|
CreateContract: true,
|
|
}
|
|
}
|
|
|
|
func AccountPermissionsFromStrings(perms *BasePermissionsString, roles []string) (*AccountPermissions, error) {
|
|
base := NewBasePermissions()
|
|
permRv := reflect.ValueOf(perms)
|
|
for i := uint(0); i < uint(permRv.NumField()); i++ {
|
|
v := permRv.Field(int(i)).Bool()
|
|
base.Set(1<<i, v)
|
|
}
|
|
|
|
aP := &AccountPermissions{
|
|
Base: base,
|
|
Roles: make([]string, len(roles)),
|
|
}
|
|
copy(aP.Roles, roles)
|
|
return aP, nil
|
|
}
|
|
|
|
func AccountPermissionsToStrings(aP *AccountPermissions) (*BasePermissionsString, []string, error) {
|
|
perms := new(BasePermissionsString)
|
|
permsRv := reflect.ValueOf(perms).Elem()
|
|
for i := uint(0); i < NumBasePermissions; i++ {
|
|
pf := PermFlag(1 << i)
|
|
if aP.Base.IsSet(pf) {
|
|
// won't err if the bit is set
|
|
v, _ := aP.Base.Get(pf)
|
|
f := permsRv.Field(int(i))
|
|
f.SetBool(v)
|
|
}
|
|
}
|
|
roles := make([]string, len(aP.Roles))
|
|
copy(roles, aP.Roles)
|
|
return perms, roles, nil
|
|
}
|
|
|
|
func PermFlagToString(pf PermFlag) (perm string, err error) {
|
|
switch pf {
|
|
case Root:
|
|
perm = "root"
|
|
case Send:
|
|
perm = "send"
|
|
case Call:
|
|
perm = "call"
|
|
case CreateContract:
|
|
perm = "create_contract"
|
|
case CreateAccount:
|
|
perm = "create_account"
|
|
case Bond:
|
|
perm = "bond"
|
|
case Name:
|
|
perm = "name"
|
|
default:
|
|
err = fmt.Errorf("Unknown permission flag %b", pf)
|
|
}
|
|
return
|
|
}
|
|
|
|
func PermStringToFlag(perm string) (pf PermFlag, err error) {
|
|
switch perm {
|
|
case "root":
|
|
pf = Root
|
|
case "send":
|
|
pf = Send
|
|
case "call":
|
|
pf = Call
|
|
case "create_contract":
|
|
pf = CreateContract
|
|
case "create_account":
|
|
pf = CreateAccount
|
|
case "bond":
|
|
pf = Bond
|
|
case "name":
|
|
pf = Name
|
|
default:
|
|
err = fmt.Errorf("Unknown permission %s", perm)
|
|
}
|
|
return
|
|
}
|