1// Package authorizable is an extension of p/nt/ownable;2// It allows the user to instantiate an Authorizable struct, which extends3// p/nt/ownable with a list of users that are authorized for something.4// By using authorizable, you have a superuser (ownable), as well as another5// authorization level, which can be used for adding moderators or similar to your realm.6package authorizable78import (9 "gno.land/p/nt/bptree/v0"10 "gno.land/p/nt/ownable/v0"11 "gno.land/p/nt/ufmt/v0"12)1314type Authorizable struct {15 *ownable.Ownable // owner in ownable is superuser16 authorized *bptree.BPTree // chain.Addr > struct{}{}17}1819// New creates an Authorizable from an existing *ownable.Ownable.20// The owner is automatically added to the auth list.21//22// Example construction:23//24// authorizable.New(ownable.NewWithAddress(addr))25func New(o *ownable.Ownable) *Authorizable {26 a := &Authorizable{27 Ownable: o,28 authorized: bptree.NewBPTree32(),29 }3031 // Add owner to auth list32 a.authorized.Set(a.Owner().String(), struct{}{})33 return a34}3536// AddToAuthList adds addr to the auth list. rlm must be the caller's37// own captured cur; rlm.Previous().Address() must equal the superuser38// (the underlying Ownable's owner).39func (a *Authorizable) AddToAuthList(_ int, rlm realm, addr address) error {40 if !rlm.IsCurrent() {41 return ErrNotSuperuser42 }43 if !a.OwnedBy(rlm.Previous().Address()) {44 return ErrNotSuperuser45 }46 return a.addToAuthList(addr)47}4849func (a *Authorizable) addToAuthList(addr address) error {50 if a.authorized.Has(addr.String()) {51 return ErrAlreadyInList52 }5354 a.authorized.Set(addr.String(), struct{}{})5556 return nil57}5859// DeleteFromAuthList removes addr from the auth list. rlm must be the60// caller's own captured cur; rlm.Previous().Address() must equal the61// superuser (the underlying Ownable's owner).62func (a *Authorizable) DeleteFromAuthList(_ int, rlm realm, addr address) error {63 if !rlm.IsCurrent() {64 return ErrNotSuperuser65 }66 if !a.OwnedBy(rlm.Previous().Address()) {67 return ErrNotSuperuser68 }69 return a.deleteFromAuthList(addr)70}7172func (a *Authorizable) deleteFromAuthList(addr address) error {73 if !a.authorized.Has(addr.String()) {74 return ErrNotInAuthList75 }7677 if _, removed := a.authorized.Remove(addr.String()); !removed {78 str := ufmt.Sprintf("authorizable: could not remove %s from auth list", addr.String())79 panic(str)80 }8182 return nil83}8485// OnAuthList reports whether rlm.Address() is on the auth list. rlm86// must be the caller's own captured cur (asserted via rlm.IsCurrent()).87// Pre-migration shape used unsafe.CurrentRealm().Address() — vulnerable88// to the .Title()-class read where a non-crossing wrapper made the walk89// return the wrong realm. Explicit rlm closes that.90func (a *Authorizable) OnAuthList(_ int, rlm realm) error {91 if !rlm.IsCurrent() {92 return ErrNotInAuthList93 }94 return a.onAuthList(rlm.Address())95}9697// PreviousOnAuthList reports whether rlm.Previous().Address() — the98// realm that crossed into the caller — is on the auth list. Same rlm99// contract as OnAuthList.100func (a *Authorizable) PreviousOnAuthList(_ int, rlm realm) error {101 if !rlm.IsCurrent() {102 return ErrNotInAuthList103 }104 return a.onAuthList(rlm.Previous().Address())105}106107func (a *Authorizable) onAuthList(caller address) error {108 if !a.authorized.Has(caller.String()) {109 return ErrNotInAuthList110 }111 return nil112}113114func (a Authorizable) AssertOnAuthList(_ int, rlm realm) {115 if err := a.OnAuthList(0, rlm); err != nil {116 panic(err)117 }118}119120func (a Authorizable) AssertPreviousOnAuthList(_ int, rlm realm) {121 if err := a.PreviousOnAuthList(0, rlm); err != nil {122 panic(err)123 }124}125Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.