authorizable.gno
3.58 Kb · 124 lines
1// Package authorizable is an extension of p/nt/ownable;
2// It allows the user to instantiate an Authorizable struct, which extends
3// 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 another
5// authorization level, which can be used for adding moderators or similar to your realm.
6package authorizable
7
8import (
9 "gno.land/p/nt/bptree/v0"
10 "gno.land/p/nt/ownable/v0"
11 "gno.land/p/nt/ufmt/v0"
12)
13
14type Authorizable struct {
15 *ownable.Ownable // owner in ownable is superuser
16 authorized *bptree.BPTree // chain.Addr > struct{}{}
17}
18
19// 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 }
30
31 // Add owner to auth list
32 a.authorized.Set(a.Owner().String(), struct{}{})
33 return a
34}
35
36// AddToAuthList adds addr to the auth list. rlm must be the caller's
37// own captured cur; rlm.Previous().Address() must equal the superuser
38// (the underlying Ownable's owner).
39func (a *Authorizable) AddToAuthList(_ int, rlm realm, addr address) error {
40 if !rlm.IsCurrent() {
41 return ErrNotSuperuser
42 }
43 if !a.OwnedBy(rlm.Previous().Address()) {
44 return ErrNotSuperuser
45 }
46 return a.addToAuthList(addr)
47}
48
49func (a *Authorizable) addToAuthList(addr address) error {
50 if _, exists := a.authorized.Get(addr.String()); exists {
51 return ErrAlreadyInList
52 }
53
54 a.authorized.Set(addr.String(), struct{}{})
55
56 return nil
57}
58
59// DeleteFromAuthList removes addr from the auth list. rlm must be the
60// caller's own captured cur; rlm.Previous().Address() must equal the
61// superuser (the underlying Ownable's owner).
62func (a *Authorizable) DeleteFromAuthList(_ int, rlm realm, addr address) error {
63 if !rlm.IsCurrent() {
64 return ErrNotSuperuser
65 }
66 if !a.OwnedBy(rlm.Previous().Address()) {
67 return ErrNotSuperuser
68 }
69 return a.deleteFromAuthList(addr)
70}
71
72func (a *Authorizable) deleteFromAuthList(addr address) error {
73 if !a.authorized.Has(addr.String()) {
74 return ErrNotInAuthList
75 }
76
77 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 }
81
82 return nil
83}
84
85// OnAuthList reports whether rlm.Address() is on the auth list. rlm
86// must be the caller's own captured cur (asserted via rlm.IsCurrent()).
87// Pre-migration shape used unsafe.CurrentRealm().Address() — vulnerable
88// to the .Title()-class read where a non-crossing wrapper made the walk
89// return the wrong realm. Explicit rlm closes that.
90func (a *Authorizable) OnAuthList(_ int, rlm realm) error {
91 if !rlm.IsCurrent() {
92 return ErrNotInAuthList
93 }
94 return a.onAuthList(rlm.Address())
95}
96
97// PreviousOnAuthList reports whether rlm.Previous().Address() — the
98// realm that crossed into the caller — is on the auth list. Same rlm
99// contract as OnAuthList.
100func (a *Authorizable) PreviousOnAuthList(_ int, rlm realm) error {
101 if !rlm.IsCurrent() {
102 return ErrNotInAuthList
103 }
104 return a.onAuthList(rlm.Previous().Address())
105}
106
107func (a *Authorizable) onAuthList(caller address) error {
108 if !a.authorized.Has(caller.String()) {
109 return ErrNotInAuthList
110 }
111 return nil
112}
113
114func (a Authorizable) AssertOnAuthList(_ int, rlm realm) {
115 if err := a.OnAuthList(0, rlm); err != nil {
116 panic(err)
117 }
118}
119
120func (a Authorizable) AssertPreviousOnAuthList(_ int, rlm realm) {
121 if err := a.PreviousOnAuthList(0, rlm); err != nil {
122 panic(err)
123 }
124}