@inkwell.ar/sdk
Version:
SDK for interacting with the Inkwell Blog CRUD AO process using aoconnect for deployment and interactions
462 lines (364 loc) • 13.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ACCESS_CONTROL_CONTRACT = void 0;
// AccessControl module for browser deployment
exports.ACCESS_CONTROL_CONTRACT = `-- AccessControl library for AO processes
-- Similar to OpenZeppelin's AccessControl contract
Name = "Access Control"
Author = "@7i7o"
local AccessControl = {}
local is_initialized = false
-- Storage for roles and role members
local roles = {}
local role_members = {}
local role_admins = {}
-- Events (for logging)
local is_log_enabled = true
local events = {}
-- Predefined common roles (similar to OpenZeppelin's common roles)
AccessControl.ROLES = {
-- DEFAULT_ADMIN_ROLE acts similar to a "root" admin
DEFAULT_ADMIN = "DEFAULT_ADMIN_ROLE",
MINTER = "MINTER_ROLE",
BURNER = "BURNER_ROLE",
PAUSER = "PAUSER_ROLE",
MANAGER = "MANAGER_ROLE",
MODERATOR = "MODERATOR_ROLE",
UPGRADER = "UPGRADER_ROLE",
ADMIN = "ADMIN_ROLE",
EDITOR = "EDITOR_ROLE",
VIEWER = "VIEWER_ROLE"
}
-- Constants for better maintainability
AccessControl.ERROR_MESSAGES = {
NOT_INITIALIZED = "AccessControl not initialized",
INVALID_ACCOUNT = "Invalid account",
INVALID_ROLE = "Invalid role",
ROLE_EXISTS = "Role already exists",
ROLE_NOT_EXISTS = "Role does not exist",
ACCOUNT_HAS_ROLE = "Account already has role",
ACCOUNT_NO_ROLE = "Account does not have role",
MISSING_ROLE = "AccessControl: account %s is missing role %s",
NOT_ADMIN = "AccessControl: account %s is not admin of role %s",
CANNOT_REMOVE_LAST_ADMIN = "Cannot remove last DEFAULT_ADMIN"
}
-- Role logger helper
local function log_role_update(caller, message, role, old_admin_role, new_admin_role)
if is_log_enabled then
table.insert(events, {
type = message,
role = role,
oldAdminRole = old_admin_role,
newAdminRole = new_admin_role,
caller = caller,
timestamp = os.time()
})
end
end
-- Account role logger helper
local function log_role_account(caller, message, role, account)
if is_log_enabled then
table.insert(events, {
type = message,
role = role,
account = account,
caller = caller,
timestamp = os.time()
})
end
end
-- Check if module is initialized
local function check_initialized()
if not is_initialized then
return false, AccessControl.ERROR_MESSAGES.NOT_INITIALIZED
end
return true, nil
end
-- Check if a role exists
function AccessControl.role_exists(role)
return roles[role] == true
end
-- Validators
local function validate_account(account)
if not account or type(account) ~= "string" or account == "" then
return false, AccessControl.ERROR_MESSAGES.INVALID_ACCOUNT
end
return true, nil
end
local function validate_role(role)
if not role or type(role) ~= "string" or role == "" then
return false, AccessControl.ERROR_MESSAGES.INVALID_ROLE
end
if not AccessControl.role_exists(role) then
return false, AccessControl.ERROR_MESSAGES.ROLE_NOT_EXISTS
end
return true, nil
end
-- Combined validation helper
local function validate_inputs(account, role)
local success, error = check_initialized()
if not success then return false, error end
success, error = validate_account(account)
if not success then return false, error end
success, error = validate_role(role)
if not success then return false, error end
return true, nil
end
-- Check if an address has a specific role
function AccessControl.has_role(account, role)
local success, error = validate_inputs(account, role)
if not success then return false, error end
return role_members[role] and role_members[role][account] == true, nil
end
-- Get the admin role for a given role
function AccessControl.get_role_admin(role)
local success, error = check_initialized()
if not success then return nil, error end
success, error = validate_role(role)
if not success then return nil, error end
return role_admins[role], nil
end
-- Get user roles
function AccessControl.get_user_roles(account)
local success, error = check_initialized()
if not success then return nil, error end
local user_roles = {}
for role, exists in pairs(roles) do
if exists and role_members[role][account] then
table.insert(user_roles, role)
end
end
return user_roles, nil
end
-- Check if caller can administer a role
local function can_administer_role(caller, role)
local admin_role, err = AccessControl.get_role_admin(role)
if err then return false, err end
local has_admin_role, _ = AccessControl.has_role(caller, admin_role)
local has_default_admin, _ = AccessControl.has_role(caller, AccessControl.ROLES.DEFAULT_ADMIN)
return has_admin_role or has_default_admin,
string.format(AccessControl.ERROR_MESSAGES.NOT_ADMIN, caller, role)
end
-- Get all roles
function AccessControl.get_all_roles()
local success, error = check_initialized()
if not success then return nil, error end
local all_roles = {}
for role, exists in pairs(roles) do
if exists then
table.insert(all_roles, role)
end
end
return all_roles, nil
end
-- Get all members of a role
function AccessControl.get_role_members(role)
local success, error = check_initialized()
if not success then return nil, error end
success, error = validate_role(role)
if not success then return nil, error end
local members = {}
for account, is_member in pairs(role_members[role]) do
if is_member then table.insert(members, account) end
end
return members, nil
end
-- Get the number of members in a role
function AccessControl.get_role_member_count(role)
local success, error = check_initialized()
if not success then return 0, error end
success, error = validate_role(role)
if not success then return 0, error end
local count = 0
for _, is_member in pairs(role_members[role]) do
if is_member then count = count + 1 end
end
return count, nil
end
-- Initialize the access control system
function AccessControl.init(caller, owner, is_log_disabled)
if is_initialized then
return false, "Already initialized"
end
local success, error = validate_account(caller)
if not success then return false, error end
success, error = validate_account(owner)
if not success then return false, error end
-- Set up the default admin role
roles[AccessControl.ROLES.DEFAULT_ADMIN] = true
role_members[AccessControl.ROLES.DEFAULT_ADMIN] = {}
role_admins[AccessControl.ROLES.DEFAULT_ADMIN] = AccessControl.ROLES.DEFAULT_ADMIN
-- Grant default admin role to the owner
role_members[AccessControl.ROLES.DEFAULT_ADMIN][owner] = true
-- Mark module as initialized
is_initialized = true
-- Configure logging
if is_log_disabled then
is_log_enabled = false
end
log_role_update(caller, "RoleCreated", AccessControl.ROLES.DEFAULT_ADMIN, nil, AccessControl.ROLES.DEFAULT_ADMIN)
log_role_account(caller, "RoleGranted", AccessControl.ROLES.DEFAULT_ADMIN, owner)
return true, nil
end
-- Create a new role
function AccessControl.create_role(caller, role, admin_role)
admin_role = admin_role or AccessControl.ROLES.DEFAULT_ADMIN
local success, error = validate_inputs(caller, admin_role)
if not success then return false, error end
-- Check if role already exists
if AccessControl.role_exists(role) then
return false, AccessControl.ERROR_MESSAGES.ROLE_EXISTS
end
-- Check if caller has permission (only admins of admin_role can add "children" roles)
local can_admin, error = can_administer_role(caller, admin_role)
if not can_admin then return false, error end
roles[role] = true
role_members[role] = {}
role_admins[role] = admin_role
-- Log event
log_role_update(caller, "RoleCreated", role, nil, admin_role)
return true, nil
end
-- Grant a role to an account
function AccessControl.grant_role(caller, role, account)
local success, error = validate_account(caller)
if not success then return false, error end
success, error = validate_inputs(account, role)
if not success then return false, error end
local can_admin, admin_err = can_administer_role(caller, role)
if not can_admin then return false, admin_err end
local already_has_role, _ = AccessControl.has_role(account, role)
if already_has_role then
return true, nil
end
role_members[role][account] = true
-- Log event
log_role_account(caller, "RoleGranted", role, account)
return true, nil
end
-- Check if removing this role member would leave no admins
local function would_remove_last_admin(role, account)
if role ~= AccessControl.ROLES.DEFAULT_ADMIN then
return false
end
local count = AccessControl.get_role_member_count(role)
return count == 1 and role_members[role][account] == true
end
-- Revoke a role from an account
function AccessControl.revoke_role(caller, role, account)
local success, error = validate_inputs(account, role)
if not success then return false, error end
local can_admin, err = can_administer_role(caller, role)
if not can_admin then return false, err end
local has_role, _ = AccessControl.has_role(account, role)
if not has_role then
return true, nil
end
-- Prevent removing the last DEFAULT_ADMIN
if would_remove_last_admin(role, account) then
return false, AccessControl.ERROR_MESSAGES.CANNOT_REMOVE_LAST_ADMIN
end
role_members[role][account] = nil
-- Log event
log_role_account(caller, "RoleRevoked", role, account)
return true, nil
end
-- Renounce a role (account renounces their own role)
function AccessControl.renounce_role(caller, role)
local success, error = validate_inputs(caller, role)
if not success then return false, error end
local has_role, _ = AccessControl.has_role(caller, role)
if not has_role then
return false, AccessControl.ERROR_MESSAGES.ACCOUNT_NO_ROLE
end
-- Prevent renouncing if you're the last DEFAULT_ADMIN
if would_remove_last_admin(role, caller) then
return false, AccessControl.ERROR_MESSAGES.CANNOT_REMOVE_LAST_ADMIN
end
role_members[role][caller] = nil
-- Log event
log_role_account(caller, "RoleRenounced", role, caller)
return true, nil
end
-- Set a new admin for a role
function AccessControl.set_role_admin(caller, role, admin_role)
-- can_administer_role already validates caller and role inputs
local can_admin, err = can_administer_role(caller, role)
if not can_admin then return false, err end
local success, error = validate_role(admin_role)
if not success then return false, "Admin " .. error end
local old_admin_role = role_admins[role]
role_admins[role] = admin_role
-- Log event
log_role_update(caller, "RoleAdminChanged", role, old_admin_role, admin_role)
return true, nil
end
-- Modifier function to check if caller has a specific role
function AccessControl.only_role(caller, role)
local has_role, error = AccessControl.has_role(caller, role)
if not has_role then
return false, error or string.format(AccessControl.ERROR_MESSAGES.MISSING_ROLE, caller or "unknown", role)
end
return true, nil
end
-- Modifier function to check if caller is admin of a role
function AccessControl.only_role_admin(caller, role)
-- can_administer_role already validates account and role
local can_admin, err = can_administer_role(caller, role)
return can_admin, err
end
-- Get all events (for debugging/logging)
function AccessControl.get_events()
local success, error = check_initialized()
if not success then return nil, error end
return events, nil
end
-- Clear events (optional, for memory management)
function AccessControl.clear_events()
local success, error = check_initialized()
if not success then return false, error end
events = {}
return true, nil
end
-- Helper function to setup common roles
function AccessControl.setup_common_roles(caller)
local success, error = AccessControl.only_role(caller, AccessControl.ROLES.DEFAULT_ADMIN)
if not success then return false, error end
-- Create common roles with DEFAULT_ADMIN as their admin
for _, role in pairs(AccessControl.ROLES) do
if role ~= AccessControl.ROLES.DEFAULT_ADMIN then
AccessControl.create_role(caller, role, AccessControl.ROLES.DEFAULT_ADMIN)
end
end
return true, nil
end
-- Additional utility functions
-- Check if the system is initialized
function AccessControl.is_initialized()
return is_initialized
end
-- Get system status
function AccessControl.get_status()
return {
initialized = is_initialized,
logging_enabled = is_log_enabled,
total_roles = #AccessControl.get_all_roles() or 0,
total_events = #events
}
end
-- Bulk operations for efficiency
function AccessControl.bulk_grant_role(caller, role, accounts)
local success, error = check_initialized()
if not success then return false, error end
if not accounts or type(accounts) ~= "table" then
return false, "Accounts must be a table"
end
local results = {}
for _, account in ipairs(accounts) do
local result, err = AccessControl.grant_role(caller, role, account)
table.insert(results, { account = account, success = result, error = err })
end
return true, results
end
return AccessControl`;
//# sourceMappingURL=access-control-contract.js.map