rbac.gno

package rbac

import (
	"chain"

	"gno.land/r/gnoswap/access"

	prbac "gno.land/p/gnoswap/rbac"
	ufmt "gno.land/p/nt/ufmt/v0"
)

var manager *prbac.RBAC

func init(cur realm) {
	initRbac(cur)
}

// initRbac initializes RBAC manager with default admin and role mappings.
func initRbac(cur realm) {
	manager = prbac.NewRBACWithAddress(ADMIN)

	// Prepare initial roles for one-time initialization
	for role, addr := range _defaultRoleAddresses {
		roleName := role.String()
		err := manager.RegisterRole(roleName, addr)
		if err != nil {
			panic(makeErrorWithDetails(
				err.Error(),
				ufmt.Sprintf("role name: %s, address: %s", roleName, addr.String()),
			))
		}

		// Update access package with the role address
		access.SetRoleAddress(cross(cur), roleName, addr)
	}
}

// RegisterRole registers a new role in the RBAC system.
//
// Parameters:
//   - roleName: name of the role to register
//   - roleAddress: address to assign to the role
//
// Only callable by admin or governance.
func RegisterRole(cur realm, roleName string, roleAddress address) {
	prev := cur.Previous()
	caller := prev.Address()
	assertIsAdminOrGovernance(caller)
	assertIsValidRoleName(roleName)
	assertIsValidAddress(roleAddress)

	err := manager.RegisterRole(roleName, roleAddress)
	if err != nil {
		if err.Error() == "role already exists" {
			panic(ufmt.Sprintf("role %s already exists", roleName))
		}
		panic(makeErrorWithDetails(
			errInvalidRoleName,
			ufmt.Sprintf("role name: %s", roleName),
		))
	}

	// Set the role in access control
	access.SetRoleAddress(cross(cur), roleName, roleAddress)
	chain.Emit(
		"RegisterRole",
		"prevAddr", caller.String(),
		"prevRealm", prev.PkgPath(),
		"roleName", roleName,
		"roleAddress", roleAddress.String(),
	)
}

// UpdateRoleAddress updates the address assigned to a role.
//
// Parameters:
//   - roleName: name of the role
//   - addr: new address for the role
//
// Only callable by admin or governance.
func UpdateRoleAddress(cur realm, roleName string, addr address) {
	prev := cur.Previous()
	caller := prev.Address()
	assertIsAdminOrGovernance(caller)

	assertIsValidRoleName(roleName)
	assertIsValidAddress(addr)
	assertNotAdminRole(roleName)

	err := manager.UpdateRoleAddress(roleName, addr)
	if err != nil {
		panic(makeErrorWithDetails(
			err.Error(),
			ufmt.Sprintf("role name: %s, address: %s", roleName, addr.String()),
		))
	}

	// Set the role address in access control
	access.SetRoleAddress(cross(cur), roleName, addr)

	chain.Emit(
		"UpdateRoleAddress",
		"prevAddr", caller.String(),
		"prevRealm", prev.PkgPath(),
		"roleName", roleName,
		"roleAddress", addr.String(),
	)
}

// RemoveRole removes a role from the RBAC system.
//
// Parameters:
//   - roleName: name of the role to remove
//
// Only callable by admin or governance.
func RemoveRole(cur realm, roleName string) {
	prev := cur.Previous()
	caller := prev.Address()
	assertIsAdminOrGovernance(caller)
	assertIsValidRoleName(roleName)
	assertNotAdminRole(roleName)

	err := manager.RemoveRole(roleName)
	if err != nil {
		panic(makeErrorWithDetails(
			err.Error(),
			ufmt.Sprintf("role name: %s", roleName),
		))
	}

	// Remove the role from access control
	access.RemoveRole(cross(cur), roleName)
	chain.Emit(
		"RemoveRole",
		"prevAddr", caller.String(),
		"prevRealm", prev.PkgPath(),
		"roleName", roleName,
		"roleAddress", "",
	)
}

// GetRoleAddress returns the address assigned to roleName.
func GetRoleAddress(roleName string) (address, error) {
	return manager.GetRoleAddress(roleName)
}