Files
metabuilder/packages/role_editor/seed/scripts/db/operations.lua
2025-12-30 21:59:30 +00:00

384 lines
9.2 KiB
Lua

-- role_editor/seed/scripts/db/operations.lua
-- DBAL operations for Role management
-- Uses Permission entity from Prisma schema
-- @module role_editor.db.operations
local M = {}
local json = require('json')
-- Permission levels
M.LEVELS = {
PUBLIC = 0,
USER = 1,
MODERATOR = 2,
ADMIN = 3,
GOD = 4,
SUPERGOD = 5,
SYSTEM = 6,
}
M.LEVEL_NAMES = {
[0] = 'Public',
[1] = 'User',
[2] = 'Moderator',
[3] = 'Admin',
[4] = 'God',
[5] = 'Supergod',
[6] = 'System',
}
---------------------------------------------------------------------------
-- ROLE/PERMISSION OPERATIONS
---------------------------------------------------------------------------
---@class RoleCreateParams
---@field tenantId string
---@field name string
---@field level number 0-6
---@field description string|nil
---@field permissions table|nil Array of permission strings
---@field color string|nil Display color
---Create a new role
---@param dbal table DBAL client instance
---@param params RoleCreateParams
---@return table Created role
function M.createRole(dbal, params)
return dbal:create('Role', {
tenantId = params.tenantId,
name = params.name,
level = params.level or 1,
description = params.description,
permissions = params.permissions and json.encode(params.permissions) or '[]',
color = params.color or '#808080',
isDefault = false,
createdAt = os.time() * 1000,
updatedAt = os.time() * 1000,
})
end
---Get role by ID
---@param dbal table
---@param roleId string
---@return table|nil Role
function M.getRole(dbal, roleId)
local role = dbal:read('Role', roleId)
if role and role.permissions then
role.permissions = json.decode(role.permissions)
end
return role
end
---Get role by name
---@param dbal table
---@param tenantId string
---@param name string
---@return table|nil Role
function M.getRoleByName(dbal, tenantId, name)
local role = dbal:findFirst('Role', {
where = { tenantId = tenantId, name = name },
})
if role and role.permissions then
role.permissions = json.decode(role.permissions)
end
return role
end
---List all roles for a tenant
---@param dbal table
---@param tenantId string
---@return table[] Roles sorted by level
function M.listRoles(dbal, tenantId)
local result = dbal:list('Role', {
where = { tenantId = tenantId },
orderBy = { level = 'asc' },
take = 100,
})
local roles = result.items or {}
-- Parse permissions JSON
for _, role in ipairs(roles) do
if role.permissions and type(role.permissions) == 'string' then
role.permissions = json.decode(role.permissions)
end
end
return roles
end
---Update role
---@param dbal table
---@param roleId string
---@param updates table
---@return table Updated role
function M.updateRole(dbal, roleId, updates)
if updates.permissions and type(updates.permissions) == 'table' then
updates.permissions = json.encode(updates.permissions)
end
updates.updatedAt = os.time() * 1000
return dbal:update('Role', roleId, updates)
end
---Add permission to role
---@param dbal table
---@param roleId string
---@param permission string
---@return table Updated role
function M.addPermission(dbal, roleId, permission)
local role = M.getRole(dbal, roleId)
if not role then
error('Role not found: ' .. roleId)
end
local perms = role.permissions or {}
-- Check if already has permission
for _, p in ipairs(perms) do
if p == permission then
return role
end
end
table.insert(perms, permission)
return M.updateRole(dbal, roleId, { permissions = perms })
end
---Remove permission from role
---@param dbal table
---@param roleId string
---@param permission string
---@return table Updated role
function M.removePermission(dbal, roleId, permission)
local role = M.getRole(dbal, roleId)
if not role then
error('Role not found: ' .. roleId)
end
local perms = role.permissions or {}
local newPerms = {}
for _, p in ipairs(perms) do
if p ~= permission then
table.insert(newPerms, p)
end
end
return M.updateRole(dbal, roleId, { permissions = newPerms })
end
---Check if role has permission
---@param dbal table
---@param roleId string
---@param permission string
---@return boolean
function M.hasPermission(dbal, roleId, permission)
local role = M.getRole(dbal, roleId)
if not role then
return false
end
for _, p in ipairs(role.permissions or {}) do
if p == permission or p == '*' then
return true
end
end
return false
end
---Delete role
---@param dbal table
---@param roleId string
---@return boolean Success
function M.deleteRole(dbal, roleId)
return dbal:delete('Role', roleId)
end
---------------------------------------------------------------------------
-- USER ROLE ASSIGNMENT
---------------------------------------------------------------------------
---Assign role to user
---@param dbal table
---@param userId string
---@param roleId string
---@return table Created assignment
function M.assignRoleToUser(dbal, userId, roleId)
-- Check if already assigned
local existing = dbal:findFirst('UserRole', {
where = { userId = userId, roleId = roleId },
})
if existing then
return existing
end
return dbal:create('UserRole', {
userId = userId,
roleId = roleId,
createdAt = os.time() * 1000,
})
end
---Remove role from user
---@param dbal table
---@param userId string
---@param roleId string
---@return boolean Success
function M.removeRoleFromUser(dbal, userId, roleId)
local assignment = dbal:findFirst('UserRole', {
where = { userId = userId, roleId = roleId },
})
if assignment then
return dbal:delete('UserRole', assignment.id)
end
return false
end
---Get user's roles
---@param dbal table
---@param userId string
---@return table[] Roles
function M.getUserRoles(dbal, userId)
local assignments = dbal:list('UserRole', {
where = { userId = userId },
take = 100,
})
local roles = {}
for _, assignment in ipairs(assignments.items or {}) do
local role = M.getRole(dbal, assignment.roleId)
if role then
table.insert(roles, role)
end
end
return roles
end
---Get effective permission level for user
---@param dbal table
---@param userId string
---@return number Maximum level from all roles
function M.getEffectiveLevel(dbal, userId)
local roles = M.getUserRoles(dbal, userId)
local maxLevel = 0
for _, role in ipairs(roles) do
if (role.level or 0) > maxLevel then
maxLevel = role.level
end
end
return maxLevel
end
---Get all permissions for user
---@param dbal table
---@param userId string
---@return table Unique permissions
function M.getAllUserPermissions(dbal, userId)
local roles = M.getUserRoles(dbal, userId)
local permSet = {}
for _, role in ipairs(roles) do
for _, perm in ipairs(role.permissions or {}) do
permSet[perm] = true
end
end
local perms = {}
for perm, _ in pairs(permSet) do
table.insert(perms, perm)
end
table.sort(perms)
return perms
end
---Check if user has specific permission
---@param dbal table
---@param userId string
---@param permission string
---@return boolean
function M.userHasPermission(dbal, userId, permission)
local perms = M.getAllUserPermissions(dbal, userId)
for _, p in ipairs(perms) do
if p == permission or p == '*' then
return true
end
end
return false
end
---------------------------------------------------------------------------
-- DEFAULT ROLES
---------------------------------------------------------------------------
---Create default roles for a tenant
---@param dbal table
---@param tenantId string
---@return table[] Created roles
function M.createDefaultRoles(dbal, tenantId)
local roles = {}
-- Public role
table.insert(roles, M.createRole(dbal, {
tenantId = tenantId,
name = 'Guest',
level = 0,
description = 'Unauthenticated visitor',
permissions = {'view:public'},
color = '#9E9E9E',
}))
-- User role
table.insert(roles, M.createRole(dbal, {
tenantId = tenantId,
name = 'User',
level = 1,
description = 'Registered user',
permissions = {'view:public', 'view:private', 'edit:own'},
color = '#2196F3',
}))
-- Moderator role
table.insert(roles, M.createRole(dbal, {
tenantId = tenantId,
name = 'Moderator',
level = 2,
description = 'Content moderator',
permissions = {'view:public', 'view:private', 'edit:own', 'moderate:content'},
color = '#4CAF50',
}))
-- Admin role
table.insert(roles, M.createRole(dbal, {
tenantId = tenantId,
name = 'Admin',
level = 3,
description = 'Administrator',
permissions = {'view:*', 'edit:*', 'manage:users', 'manage:content'},
color = '#FF9800',
}))
-- God role
table.insert(roles, M.createRole(dbal, {
tenantId = tenantId,
name = 'God',
level = 4,
description = 'Full access',
permissions = {'*'},
color = '#F44336',
}))
return roles
end
return M