Seguridad y control de acceso
El módulo security expone actores de autenticación, alcances de autorización, políticas y almacenes de tokens. Esta página es una referencia de API con recetas parciales de autorización. Los ID de registro, actores, metadatos de solicitud, valores de token, objetos de la aplicación como user y doc y callbacks como show_admin_features proceden de la aplicación contenedora; los ejemplos no son un despliegue completo de autenticación.
Wippy se ejecuta en modo de seguridad estricto de forma predeterminada. La entrada ejecutable debe habilitar security, tener un actor y un alcance y autorizar exactamente las operaciones que llama. En particular, la construcción y los cambios de alcance necesitan security.actor.create o security.scope.create; la consulta del registro necesita security.policy.get o security.policy_group.get; el trabajo con tokens necesita security.token_store.get además del permiso específico de la operación. new_actor, new_scope, scope:with, scope:without y una adquisición de token_store con permiso denegado generan un error Lua en vez de devolver un error estructurado. Concede estos prerrequisitos en el contexto de seguridad de la entrada, en lugar de intentar recuperarte tras una denegación. Consulta Modelo de seguridad para ver la configuración.
Carga
local security = require("security")
actor
Devuelve el actor de seguridad actual del contexto de ejecución.
local actor = security.actor()
if actor then
local id = actor:id()
local meta = actor:meta()
-- Use only the fields required for authorization or application logic.
local role = meta.role
end
Devuelve: Actor|nil
Los metadatos del actor pueden contener identificadores o datos personales. No registres la tabla completa de metadatos ni copies secretos en ella.
scope
Devuelve el alcance de seguridad actual del contexto de ejecución.
local scope = security.scope()
if scope then
local policies = scope:policies()
for _, policy in ipairs(policies) do
print("Active policy:", policy:id())
end
end
Devuelve: Scope|nil
can
Verifica si el contexto actual permite una accion sobre un recurso.
-- Check read permission
if not security.can("read", "user:" .. user_id) then
return nil, errors.new("Cannot read user data"):kind(errors.PERMISSION_DENIED)
end
-- Check write permission
if not security.can("write", "order:" .. order_id) then
return nil, errors.new("Cannot modify order"):kind(errors.PERMISSION_DENIED)
end
-- Check with metadata
local allowed = security.can("delete", "document:" .. doc_id, {
owner_id = doc.owner_id,
department = doc.department
})
| Parámetro | Tipo | Descripción |
|---|---|---|
action |
string | Accion a verificar |
resource |
string | Identificador de recurso |
meta |
table | Metadatos adicionales (opcional) |
Devuelve: boolean
new_actor
Crea un nuevo actor con ID y metadatos.
-- Create user actor
local actor = security.new_actor("user:" .. user.id, {
role = user.role,
department = user.department,
email = user.email
})
-- Create service actor
local service_actor = security.new_actor("service:payment-processor", {
type = "service",
version = "1.0.0"
})
| Parámetro | Tipo | Descripción |
|---|---|---|
id |
string | Identificador único del actor |
meta |
table | Pares clave-valor de metadatos |
Devuelve: Actor
new_scope
Crea un nuevo alcance personalizado.
-- Empty scope
local scope = security.new_scope()
-- Scope with policies
local read_policy, read_err = security.policy("app:read-only")
if read_err then
return nil, read_err
end
local scope = security.new_scope({read_policy})
-- Build scope incrementally
local scope = security.new_scope()
local policy1, policy1_err = security.policy("app:read")
if policy1_err then
return nil, policy1_err
end
local policy2, policy2_err = security.policy("app:write")
if policy2_err then
return nil, policy2_err
end
scope = scope:with(policy1):with(policy2)
Cada alternativa anterior es un patrón de construcción aislado. new_scope y scope:with pueden generar un error cuando falta el contexto o se deniega el permiso; no devuelven nil, error para esas comprobaciones.
Devuelve: Scope
policy
Recupera una politica del registro.
local policy, err = security.policy("app:admin-access")
if err then
return nil, err
end
-- Evaluate policy
local result = policy:evaluate(actor, "delete", "user:123")
if result == "allow" then
-- permitted
elseif result == "deny" then
-- forbidden
else
-- undefined, check other policies
end
| Parámetro | Tipo | Descripción |
|---|---|---|
id |
string | ID de politica "namespace:name" |
Devuelve: Policy, error
named_scope
Recupera un grupo de politicas predefinido.
-- Get admin scope
local admin_scope, err = security.named_scope("app:admin")
if err then
return nil, err
end
-- Use for elevated operations
local result = admin_scope:evaluate(actor, "delete", "user:123")
Cargar un alcance no eleva el contexto de ejecución actual. Produce un valor para evaluación explícita o para una API que acepte un alcance; el autor de la llamada sigue necesitando permiso para realizar la operación protegida.
| Parámetro | Tipo | Descripción |
|---|---|---|
id |
string | ID de grupo de politicas |
Devuelve: Scope, error
token_store
Adquiere un almacen de tokens para gestionar tokens de autenticación.
local store, err = security.token_store("app:tokens")
if err then
return nil, err
end
-- Use store...
return store:close()
El autor de la llamada es propietario de un almacén de tokens adquirido hasta que se llama a close(). Ciérralo después de la última operación en cada ruta comprobada de éxito o error; los cierres repetidos son seguros. Una denegación de permiso durante la adquisición genera un error Lua, mientras que los fallos de consulta y recurso devuelven nil, error.
| Parámetro | Tipo | Descripción |
|---|---|---|
id |
string | ID de almacen de tokens "namespace:name" |
Devuelve: TokenStore, error
Métodos de Actor
| Método | Devuelve | Descripción |
|---|---|---|
actor:id() |
string | Identificador del actor |
actor:meta() |
table | Metadatos del actor |
Métodos de Scope
with / without
Agregar o eliminar politicas del alcance.
local scope = security.new_scope()
-- Add policy
local write_policy, err = security.policy("app:write")
if err then
return nil, err
end
scope = scope:with(write_policy)
-- Remove policy
scope = scope:without("app:read-only")
with y without devuelven nuevos valores de alcance inmutables y generan un error cuando no se permite security.scope.create para el recurso with o without.
evaluate
Evaluar todas las politicas en el alcance.
local result = scope:evaluate(actor, "read", "document:123")
-- "allow", "deny", or "undefined"
if result ~= "allow" then
return nil, errors.new("Access denied"):kind(errors.PERMISSION_DENIED)
end
contains
Verificar si el alcance contiene una politica.
if scope:contains("app:admin") then
show_admin_features()
end
policies
Devuelve todas las politicas en el alcance.
local policies = scope:policies()
for _, policy in ipairs(policies) do
print(policy:id())
end
Devuelve: Policy[]
Métodos de Policy
| Método | Devuelve | Descripción |
|---|---|---|
policy:id() |
string | Identificador de politica |
policy:evaluate(actor, action, resource, meta?) |
string | "allow", "deny", o "undefined" |
Métodos de TokenStore
create
Crear token de autenticación.
local actor = security.new_actor("user:123", {role = "user"})
local scope, scope_err = security.named_scope("app:default")
if scope_err then
return nil, scope_err
end
local store, store_err = security.token_store("app:tokens")
if store_err then
return nil, store_err
end
local token, err = store:create(actor, scope, {
expiration = "24h", -- or milliseconds
meta = {
login_ip = request_ip,
user_agent = user_agent
}
})
store:close()
if err then
return nil, err
end
return token
request_ip y user_agent son valores de solicitud proporcionados por la aplicación. Almacena solo los metadatos necesarios para las decisiones de seguridad, aplica límites de retención y nunca registres ni conserves el token de portador devuelto fuera del almacén de credenciales previsto.
| Parámetro | Tipo | Descripción |
|---|---|---|
actor |
Actor | Actor para el token |
scope |
Scope | Alcance de permisos |
options.expiration |
string/number | String de duración o ms |
options.meta |
table | Metadatos del token |
Devuelve: string, error
validate
Validar token y obtener actor/alcance.
local actor, scope, err = store:validate(token)
store:close()
if err then
return nil, errors.new("Invalid token"):kind(errors.PERMISSION_DENIED)
end
Aquí y más abajo, store es un handle vivo bajo propiedad del autor de la llamada y token es una credencial de portador no fiable suministrada por el llamante. No registres el token, ni siquiera en errores de validación o revocación.
Devuelve: Actor, Scope, error
revoke
Invalidar un token.
local ok, err = store:revoke(token)
store:close()
if err then
return nil, err
end
Devuelve: boolean, error
close
Liberar el recurso del almacen de tokens.
store:close()
Devuelve: boolean
Permisos
Las operaciones de seguridad estan sujetas a evaluacion de politica de seguridad.
Acciones de seguridad
| Accion | Recurso | Descripción |
|---|---|---|
security.policy.get |
ID de Policy | Acceder a definiciones de políticas |
security.policy_group.get |
ID de Group | Acceder a alcances nombrados |
security.scope.create |
custom, with, without |
Crear alcances personalizados (new_scope) y agregar/quitar politicas (scope:with, scope:without) |
security.actor.create |
ID de Actor | Crear actores |
security.token_store.get |
ID de Store | Acceder a almacenes de tokens |
security.token.validate |
ID de Store | Validar tokens |
security.token.create |
ID de Store | Crear tokens |
security.token.revoke |
ID de Store | Revocar tokens |
Consulta Modelo de seguridad para configurar políticas.
Errores
| Condición | Tipo | Reintentable |
|---|---|---|
| Sin contexto | errors.INTERNAL |
no |
| ID de almacen de tokens vacio | errors.INVALID |
no |
Permiso denegado (policy, named_scope, token create/validate/revoke) |
errors.INVALID |
no |
Permiso denegado (new_scope, new_actor, token_store, scope:with/without) |
lanzado como error de Lua | no |
| Politica no encontrada | errors.INTERNAL |
no |
| Almacen de tokens no encontrado | errors.INTERNAL |
no |
| Almacen de tokens cerrado | errors.INTERNAL |
no |
| Formato de expiracion invalido | errors.INVALID |
no |
| Validacion de token fallida | errors.INTERNAL |
no |
local store, err = security.token_store("app:tokens")
if err then
if errors.is(err, errors.INVALID) then
print("Invalid request:", err:message())
end
return nil, err
end
store:close()
Consulta Manejo de errores para trabajar con errores.
Véase también
- Modelo de seguridad - Configuración de actores, políticas y alcances
- Middleware HTTP - Firewall de endpoints y recursos