在 Ent 模式中使用行级安全
PostgreSQL 中的行级安全(RLS)允许实施表策略来根据用户角色限制访问或修改行,增强 GRANT 提供的基本SQL标准的权限。
一旦激活,每个对于表的标准访问都将遵守这些策略。如果在表上没有定义任何策略,默认规则是拒绝所有,意味着所有行都不可见、不能被修改。 这些策略可以为指定的命令、角色或两者量身定制,允许精细化管理谁可以访问或修改数据。
本指南说明如何在 Ent 类型(对象)中添加行级安全(RLS)策略,并配置模式迁移,以便使用 Atlas 将 RLS 和 Ent 模式作为单一迁移单元进行管理。
Atlas 只为专业用户提供 行级安全策略支持,使用这些功能需运行:
atlas login
安装 Atlas
要安装Atlas的最新版本,只需在终端中运行以下任一命令,或访问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
如果容器需要访问主机网络或本地目录,请使用 --net=host 标志挂载所需目录:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
下载 最新版本 并将 atlas 二进制执行文件所在目录加入到系统路径中。
登录 Atlas
$ atlas login a8m
You are now connected to "a8m" on Atlas Cloud.
复合模式
ent/schema 包主要用来定义 Ent 类型(对象),包括字段、边和逻辑等。
表策略或任何其他数据库原生对象在 Ent 模式中没有相应的表达。
为扩展 PostgreSQL 模式以包含 Ent 类型和他们的策略,可以配置 Atlas 来读取 复合模式 数据源的模式状态。 跟着以下步骤来配置你的项目:
1. 定义一个简单的模式,具有两个类型(表):users 和 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. 假设我们想基于 tenant_id 字段限制访问 users 表。我们可以在 users 表上定义行级安全(RLS)策略。以下是定义 RLS 策略的 SQL 代码:
--- 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. 最后我们创建一个简单的 atlas.hcl 配置文件,配置文件具有包含在 schema.sql 和 Ent 模式中定义的自定义安全策略的 composite_schema:
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"
}
使用
设置完复合模式后,我们可以使用 atlas schema inspect 命令来查看其表示、生成迁移或将它们应用到数据库等。以下是几个帮助你开始使用 Atlas 的几个命令:
检查模式
atlas schema inspect 命令通常用来检查数据库。然而我们也可以用它来检查 composite_schema 并打印其 SQL 表示形式:
atlas schema inspect \
--env local \
--url env://src \
--format '{{ sql . }}'
以上命令打印下述 SQL。注意模式中的 tenant_isolation 策略在 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"));
为模式生成迁移
运行以下命令为模式生成迁移:
atlas migrate diff \
--env local
注意可生成以下内容的新的迁移文件:
-- 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"));
应用迁移
运行以下命令将生成的迁移应用到数据库:
atlas migrate apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
有时需要在不生成迁移文件的时候将模式直接应用于数据库。例如,尝试模式变更、创建测试数据库等。这种情况下,可以使用下面的命令将模式直接应用于数据库:
atlas schema apply \
--env local \
--url "postgres://postgres:pass@localhost:5432/database?search_path=public&sslmode=disable"
或使用 Atlas Go SDK:
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)
}
代码示例
在配置完 Ent 模式和 RLS 策略后,我们可以打开 Ent 客户端,并在执行各类修改操作和查询时传入对应的租户ID。这确保了数据库始终遵循我们的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.
本指南的代码参见 GitHub。