Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

rbac.gno

7.78 Kb · 245 lines
  1package rbac
  2
  3import (
  4	"errors"
  5	"strings"
  6)
  7
  8// RBAC encapsulates and manages roles and their permissions.
  9// It combines role management with two-step ownership transfer functionality.
 10type RBAC struct {
 11	ownable *Ownable2Step
 12	// roles maps role names to their respective `Role` objects
 13	roles map[string]*Role
 14}
 15
 16// NewRBACWithAddress creates a new RBAC instance with addr as owner.
 17//
 18// Parameters:
 19//   - addr: Address stored as the initial owner of the RBAC instance.
 20//
 21// Returns:
 22//   - *RBAC: New RBAC manager with an empty role registry and addr as owner.
 23func NewRBACWithAddress(addr address) *RBAC {
 24	return &RBAC{
 25		ownable: newOwnable2StepWithAddress(addr),
 26		roles:   make(map[string]*Role),
 27	}
 28}
 29
 30// IsAuthorized checks if addr has the specified roleName. Returns false if the role does not exist.
 31//
 32// Parameters:
 33//   - roleName: Exact role name to look up in the role registry.
 34//   - addr: Address to compare with the address assigned to roleName.
 35//
 36// Returns:
 37//   - bool: true when roleName exists and its assigned address equals addr; false when it does not.
 38func (rb *RBAC) IsAuthorized(roleName string, addr address) bool {
 39	role, exists := rb.roles[roleName]
 40	if !exists {
 41		return false
 42	}
 43
 44	return role.IsAuthorized(addr)
 45}
 46
 47// RegisterRole registers a new role with given role name and address.
 48//
 49// Parameters:
 50//   - roleName: Role identifier; leading and trailing whitespace is removed before validation and storage.
 51//   - addr: Address initially assigned to the role; this method stores it without address validation.
 52//
 53// Errors:
 54// `RegisterRole` returns an error in the following situations:
 55//   - `ErrInvalidRoleName`: role name is an empty string or contains only whitespace
 56//   - `ErrRoleAlreadyExists`: the role to be registered already exists in RBAC.
 57//   - A system-role name may be registered when absent, but remains protected
 58//     from removal; this package does not pre-register system roles.
 59//
 60// Returns:
 61//   - error: nil when the trimmed role is registered; otherwise ErrInvalidRoleName or ErrRoleAlreadyExists.
 62func (rb *RBAC) RegisterRole(roleName string, addr address) error {
 63	roleName = strings.TrimSpace(roleName)
 64	if roleName == "" {
 65		return errors.New(ErrInvalidRoleName)
 66	}
 67
 68	if rb.existsRole(roleName) {
 69		return errors.New(ErrRoleAlreadyExists)
 70	}
 71
 72	rb.roles[roleName] = NewRole(roleName, addr)
 73
 74	return nil
 75}
 76
 77// UpdateRoleAddress assigns addr to roleName.
 78//
 79// Parameters:
 80//   - roleName: Existing role identifier; leading and trailing whitespace is removed before lookup.
 81//   - addr: Non-zero, syntactically valid address to assign to the role.
 82//
 83// Errors:
 84//   - `ErrInvalidRoleName`: role name is an empty string or contains only whitespace
 85//   - `ErrRoleDoesNotExist`: the specified role does not exist in the RBAC system
 86//   - `ErrInvalidAddress`: addr is empty or has an invalid format
 87//
 88// Returns:
 89//   - error: nil when the existing role is updated; otherwise an error identifying invalid input or a missing role.
 90func (rb *RBAC) UpdateRoleAddress(roleName string, addr address) error {
 91	roleName = strings.TrimSpace(roleName)
 92	if roleName == "" {
 93		return errors.New(ErrInvalidRoleName)
 94	}
 95
 96	role, exists := rb.roles[roleName]
 97	if !exists {
 98		return errors.New(ErrRoleDoesNotExist)
 99	}
100
101	if addr == zeroAddress || !addr.IsValid() {
102		return errors.New(ErrInvalidAddress)
103	}
104
105	role.setAddress(addr)
106
107	return nil
108}
109
110// RemoveRole removes roleName from the RBAC system.
111//
112// Parameters:
113//   - roleName: Role identifier to remove; leading and trailing whitespace is removed before lookup.
114//
115// Errors:
116//   - `ErrInvalidRoleName`: role name is an empty string or contains only whitespace
117//   - `ErrRoleDoesNotExist`: the specified role does not exist in the RBAC system
118//   - `ErrCannotRemoveSystemRole`: attempting to remove a system role (e.g., admin, governance, pool, etc.)
119//
120// Returns:
121//   - error: nil when a non-system role is removed; otherwise an error for invalid, missing, or protected roles.
122func (rb *RBAC) RemoveRole(roleName string) error {
123	roleName = strings.TrimSpace(roleName)
124	if roleName == "" {
125		return errors.New(ErrInvalidRoleName)
126	}
127
128	if !rb.existsRole(roleName) {
129		return errors.New(ErrRoleDoesNotExist)
130	}
131
132	// Check if it's a system role
133	if IsSystemRole(roleName) {
134		return errors.New(ErrCannotRemoveSystemRole)
135	}
136
137	// Simply delete the role since permissions are no longer managed here
138	delete(rb.roles, roleName)
139
140	return nil
141}
142
143// GetAllRoleAddresses returns a map of all role names to their assigned addresses.
144//
145// Returns:
146//   - map[string]address: Newly allocated map containing each registered role name and its assigned address.
147func (rb *RBAC) GetAllRoleAddresses() map[string]address {
148	addresses := make(map[string]address)
149
150	for roleName, role := range rb.roles {
151		addresses[roleName] = role.Address()
152	}
153
154	return addresses
155}
156
157// GetRoleAddress returns the address assigned to roleName.
158//
159// Parameters:
160//   - roleName: Exact role name to look up; this method does not trim whitespace.
161//
162// Errors:
163//   - `ErrRoleDoesNotExist`: the specified role does not exist in the RBAC system
164//
165// Returns:
166//   - address: Address assigned to roleName, or the empty address when the role is absent.
167//   - error: nil when roleName exists; otherwise ErrRoleDoesNotExist.
168func (rb *RBAC) GetRoleAddress(roleName string) (address, error) {
169	role, exists := rb.roles[roleName]
170	if !exists {
171		return "", errors.New(ErrRoleDoesNotExist)
172	}
173
174	return role.Address(), nil
175}
176
177// Owner returns the current owner address.
178//
179// Returns:
180//   - address: Current RBAC owner address, or the empty address if ownership has been dropped.
181func (rb *RBAC) Owner() address {
182	return rb.ownable.Owner()
183}
184
185// PendingOwner returns the pending owner address during ownership transfer.
186//
187// Returns:
188//   - address: Pending owner address, or the empty address when no transfer is pending.
189func (rb *RBAC) PendingOwner() address {
190	return rb.ownable.PendingOwner()
191}
192
193// AcceptOwnershipBy completes the ownership transfer process.
194// Must be called by the pending owner.
195//
196// Parameters:
197//   - addr: Address attempting to accept ownership; it must equal the recorded pending owner.
198//
199// Errors:
200//   - `ErrNoPendingOwner`: no ownership transfer is pending
201//   - `ErrPendingUnauthorized`: addr is not the pending owner
202//
203// Returns:
204//   - error: nil when ownership is transferred to addr; otherwise ErrNoPendingOwner or ErrPendingUnauthorized.
205func (rb *RBAC) AcceptOwnershipBy(addr address) error {
206	return rb.ownable.AcceptOwnershipBy(addr)
207}
208
209// DropOwnershipBy removes the owner, effectively disabling owner-only actions.
210// This is irreversible and will prevent any future owner-only operations.
211//
212// Parameters:
213//   - addr: Address requesting the drop; it must equal the current owner.
214//
215// Errors:
216//   - `ErrUnauthorized`: addr is not the current owner
217//
218// Returns:
219//   - error: nil when owner and pending owner are cleared; otherwise ErrUnauthorized.
220func (rb *RBAC) DropOwnershipBy(addr address) error {
221	return rb.ownable.DropOwnershipBy(addr)
222}
223
224// TransferOwnershipBy initiates the two-step ownership transfer process.
225// The newOwner must call AcceptOwnershipBy to complete the transfer.
226//
227// Parameters:
228//   - newOwner: Non-zero, syntactically valid address to record as the pending owner.
229//   - caller: Address authorized to initiate the transfer; it must equal the current owner.
230//
231// Errors:
232//   - `ErrUnauthorized`: caller is not the current owner
233//   - `ErrInvalidAddress`: newOwner is empty or has an invalid format
234//
235// Returns:
236//   - error: nil when the pending owner is set; otherwise ErrUnauthorized or ErrInvalidAddress.
237func (rb *RBAC) TransferOwnershipBy(newOwner, caller address) error {
238	return rb.ownable.TransferOwnershipBy(newOwner, caller)
239}
240
241// existsRole checks if name exists in the RBAC system.
242func (rb *RBAC) existsRole(name string) bool {
243	_, exists := rb.roles[name]
244	return exists
245}