UNPKG

@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
"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