Search Apps Documentation Source Content File Folder Download Copy Actions Download 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}