docs/design-docs/design_docs/20250610-rls_design.md
Row Level Security (RLS) provides fine-grained access control at the row level for collections in Milvus. By enabling RLS and defining policies based on user identity, roles, or dynamic tags, administrators can enforce data access restrictions without modifying application logic or data structures.
| Feature | Description |
|---|---|
| Enable/Disable RLS | Toggle RLS at the collection level with runtime control |
| Enforce RLS | Enforce RLS even for superusers and administrators |
| Policy Definition | Define policies based on user ID, roles, field values, or tags |
| Multi-policy Support | Support for multiple policies per action/role combination |
| User Tag Mechanism | Use dynamic user metadata for flexible access filtering |
| Expression Language | Rich expression syntax for complex access control rules |
RLS leverages runtime user context including $current_user_name and $current_user_tags to evaluate access policies dynamically.
client.set_user_tags(
user="user_abc",
tags={
"department": "engineering",
"region": "us-west-1",
"tenant": "customer_a",
"security_level": "confidential"
}
)
| API | Description |
|---|---|
set_user_tags(user, tags) | Set or update user tags (overwrites existing) |
delete_user_tag(user, key) | Delete a specific tag key for a user |
get_user_tags(user) | Fetch all user tag information |
list_users_with_tag(key, value) | Find users with specific tag values |
Tags can be referenced in policy expressions using the following syntax:
using_expr="region == $current_user_tags['region']"
check_expr="security_level >= $current_user_tags['clearance']"
# Enable RLS for a collection
client.alter_collection_properties(
collection="my_collection",
properties={"rls.enabled": True}
)
# Disable RLS for a collection
client.alter_collection_properties(
collection="my_collection",
properties={"rls.enabled": False}
)
client.alter_collection_properties(
collection="my_collection",
properties={
"rls.enabled": True,
"rls.force": True # Applies to all users including superusers
}
)
client.create_row_policy(
collection="user_documents",
policy_name="limit_to_user",
actions=["query", "insert", "delete", "update"],
roles=["$current_user", "user_role"],
using_expr="user_id == $current_user_name",
check_expr="user_id == $current_user_name",
description="Restrict users to their own documents"
)
Policy Parameters:
collection: Target collection namepolicy_name: Unique identifier for the policyactions: List of operations this policy applies to (query, insert, delete, update)roles: List of roles this policy applies to ($current_user, admin, custom roles)using_expr: Expression for filtering data during queriescheck_expr: Expression for validating data during mutationsdescription: Optional human-readable descriptionclient.drop_row_policy(
collection="user_documents",
policy_name="limit_to_user"
)
policies = client.list_row_policies(collection="user_documents")
# Example response:
# [
# {
# "policy_name": "limit_to_user",
# "using_expr": "user_id == $current_user_name",
# "check_expr": "user_id == $current_user_name",
# "roles": ["$current_user"],
# "actions": ["query", "insert", "delete"],
# "description": "Restrict users to their own documents",
# "created_at": "2024-01-15T10:30:00Z"
# }
# ]
status = client.get_collection_properties(
collection="user_documents",
properties=["rls.enabled", "rls.force"]
)
# Returns: {"rls.enabled": True, "rls.force": False}
Scenario: A document management system where users should only see and modify their own documents.
Collection Schema:
# Collection includes a user_id field
{
"user_id": "string",
"document_name": "string",
"content": "string",
"created_at": "timestamp"
}
RLS Policy:
client.create_row_policy(
collection="user_documents",
policy_name="user_own_data",
actions=["query", "insert", "delete", "update"],
roles=["$current_user"],
using_expr="user_id == $current_user_name",
check_expr="user_id == $current_user_name",
description="Users can only access their own documents"
)
Scenario: Admins have full access, managers see department data, users see only their own data.
User Policy (restricted):
client.create_row_policy(
collection="employee_records",
policy_name="user_scope",
actions=["query", "insert", "delete", "update"],
roles=["$current_user"],
using_expr="employee_id == $current_user_name",
check_expr="employee_id == $current_user_name"
)
Manager Policy (department scope):
client.create_row_policy(
collection="employee_records",
policy_name="manager_scope",
actions=["query", "insert", "update"],
roles=["manager"],
using_expr="department == $current_user_tags['department']",
check_expr="department == $current_user_tags['department']"
)
Admin Policy (full access):
client.create_row_policy(
collection="employee_records",
policy_name="admin_full_access",
actions=["query", "insert", "delete", "update"],
roles=["admin"],
using_expr="true",
check_expr="true"
)
Scenario: SaaS application with tenant-based data isolation using user tags.
Policy:
client.create_row_policy(
collection="customer_data",
policy_name="tenant_isolation",
actions=["query", "insert", "delete", "update"],
roles=["$current_user"],
using_expr="tenant_id == $current_user_tags['tenant']",
check_expr="tenant_id == $current_user_tags['tenant']"
)
User Tag Setup:
client.set_user_tags(
user="user_123",
tags={"tenant": "acme_corp", "role": "analyst"}
)
Scenario: Documents are only accessible during business hours for non-admin users.
Policy:
client.create_row_policy(
collection="sensitive_documents",
policy_name="business_hours_access",
actions=["query"],
roles=["$current_user"],
using_expr="(hour(now()) >= 9 AND hour(now()) <= 17) OR $current_user_tags['role'] == 'admin'",
check_expr="true"
)
rls.force=True, RLS applies to everyone including superusers and administrators$current_user_name, $current_user_tags, $current_rolesnow(), hour(), date()