Uso de Seguridad a Nivel de Fila en Esquemas Ent
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
La seguridad a nivel de fila (RLS) en PostgreSQL permite que las tablas implementen políticas que limitan el acceso o modificación de filas según el rol del usuario, complementando los privilegios básicos del estándar SQL proporcionados por GRANT.
Una vez activada, todo acceso estándar a la tabla debe cumplir estas políticas. Si no se definen políticas en la tabla, se aplica una regla de denegación total, lo que significa que no se pueden ver ni modificar filas. Estas políticas pueden personalizarse para comandos específicos, roles, o ambos, permitiendo un control detallado sobre quién puede acceder o modificar datos.
Esta guía explica cómo adjuntar Políticas de Seguridad a Nivel de Fila (RLS) a tus tipos Ent (objetos) y configurar la migración del esquema para gestionar tanto RLS como el esquema Ent como una única unidad de migración usando Atlas.
El soporte de Atlas para Políticas de Seguridad a Nivel de Fila usado en esta guía está disponible exclusivamente para usuarios Pro. Para usar esta función, ejecuta:
atlas login
Instalar Atlas
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Para instalar la última versión de Atlas, simplemente ejecuta uno de los siguientes comandos en tu terminal, o visita el sitio web de Atlas:
- macOS + Linux
- Homebrew
- Docker
- Windows
curl -sSf https://atlasgo.sh | sh
brew install ariga/tap/atlas
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Iniciar sesión en Atlas
$ atlas login a8m
You are now connected to "a8m" on Atlas Cloud.
Esquema compuesto
El paquete ent/schema se usa principalmente para definir tipos Ent (objetos), sus campos, relaciones y lógica. Las políticas de tabla u otros objetos nativos de la base de datos no tienen representación en los modelos Ent.
Para extender nuestro esquema PostgreSQL incluyendo tanto los tipos Ent como sus políticas, configuramos Atlas para leer el estado del esquema desde una fuente de datos de Esquema Compuesto. Sigue estos pasos para configurarlo en tu proyecto:
1. Definamos un esquema simple con dos tipos (tablas): users y tenants:
// Tenant holds the schema definition for the Tenant entity.
type Tenant struct {
ent.Schema
}
// Fields of the Tenant.
func (Tenant) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
}
}
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name"),
field.Int("tenant_id"),
}
}
2. Supongamos que queremos limitar el acceso a la tabla users basado en el campo tenant_id. Podemos lograrlo definiendo una política de Seguridad a Nivel de Fila (RLS) en la tabla users. A continuación el código SQL que define la política RLS:
--- Enable row-level security on the users table.
ALTER TABLE "users" ENABLE ROW LEVEL SECURITY;
-- Create a policy that restricts access to rows in the users table based on the current tenant.
CREATE POLICY tenant_isolation ON "users"
USING ("tenant_id" = current_setting('app.current_tenant')::integer);
3. Finalmente, creamos un archivo de configuración atlas.hcl simple con un composite_schema que incluye tanto nuestro esquema Ent como las políticas de seguridad personalizadas definidas en schema.sql:
data "composite_schema" "app" {
# Load the ent schema first with all tables.
schema "public" {
url = "ent://ent/schema"
}
# Then, load the RLS schema.
schema "public" {
url = "file://schema.sql"
}
}
env "local" {
src = data.composite_schema.app.url
dev = "docker://postgres/15/dev?search_path=public"
}
Uso
Tras configurar nuestro esquema compuesto, podemos obtener su representación usando el comando atlas schema inspect, generar migraciones de esquema, aplicarlas a una base de datos y más. Aquí tienes algunos comandos para comenzar con Atlas:
Inspeccionar el esquema
El comando atlas schema inspect se usa comúnmente para inspeccionar bases de datos. Sin embargo, también podemos usarlo para inspeccionar nuestro composite_schema e imprimir su representación SQL:
atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'
El comando anterior imprime el siguiente SQL. Nota: la política tenant_isolation se define en el esquema después de la tabla users:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, "tenant_id" bigint NOT NULL, PRIMARY KEY ("id"));
-- Enable row-level security for "users" table
ALTER TABLE "users" ENABLE ROW LEVEL SECURITY;
-- Create policy "tenant_isolation"
CREATE POLICY "tenant_isolation" ON "users" AS PERMISSIVE FOR ALL TO PUBLIC USING (tenant_id = (current_setting('app.current_tenant'::text))::integer);
-- Create "tenants" table
CREATE TABLE "tenants" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
Generar migraciones para el esquema
Para generar una migración para el esquema, ejecuta el siguiente comando:
atlas migrate diff \
--env local
Ten en cuenta que se crea un nuevo archivo de migración con el siguiente contenido:
-- Create "users" table
CREATE TABLE "users" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, "tenant_id" bigint NOT NULL, PRIMARY KEY ("id"));
-- Enable row-level security for "users" table
ALTER TABLE "users" ENABLE ROW LEVEL SECURITY;
-- Create policy "tenant_isolation"
CREATE POLICY "tenant_isolation" ON "users" AS PERMISSIVE FOR ALL TO PUBLIC USING (tenant_id = (current_setting('app.current_tenant'::text))::integer);
-- Create "tenants" table
CREATE TABLE "tenants" ("id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, "name" character varying NOT NULL, PRIMARY KEY ("id"));
Aplicar las migraciones
Para aplicar la migración generada a una base de datos, ejecuta el siguiente comando:
atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
En ocasiones, puede ser necesario aplicar el esquema directamente a la base de datos sin generar un archivo de migración. Por ejemplo, al experimentar con cambios en el esquema, al preparar una base de datos para pruebas, etc. En estos casos, puedes usar el siguiente comando para aplicar el esquema directamente a la base de datos:
atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
O bien, usando el SDK de Go de Atlas:
ac, err := atlasexec.NewClient(".", "atlas")
if err != nil {
log.Fatalf("failed to initialize client: %w", err)
}
// Automatically update the database with the desired schema.
// Another option, is to use 'migrate apply' or 'schema apply' manually.
if _, err := ac.SchemaApply(ctx, &atlasexec.SchemaApplyParams{
Env: "local",
URL: "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable",
AutoApprove: true,
}); err != nil {
log.Fatalf("failed to apply schema changes: %w", err)
}
Ejemplo de código
Después de configurar nuestro esquema de Ent y las políticas RLS, podemos abrir un cliente de Ent y pasar a las diferentes mutaciones y consultas el ID de inquilino relevante en el que estamos trabajando. Esto asegura que la base de datos mantenga nuestra política RLS:
ctx1, ctx2 := sql.WithIntVar(ctx, "app.current_tenant", a8m.ID), sql.WithIntVar(ctx, "app.current_tenant", r3m.ID)
users1 := client.User.Query().AllX(ctx1)
// Users1 can only see users from tenant a8m.
users2 := client.User.Query().AllX(ctx2)
// Users2 can only see users from tenant r3m.
En aplicaciones reales, los usuarios pueden utilizar hooks e interceptores para
establecer la variable app.current_tenant según el contexto del usuario.
El código de esta guía se puede encontrar en GitHub.