Security
Reeflow provides a granular security model for data access, designed for both internal BI and embedded analytics.
Analytics platforms serve diverse users (internal teams, external customers, partners) each with different access requirements. Reeflow’s security model is flexible enough to support this diversity while remaining secure by default to prevent data leaks.
This page explains how Reeflow secures data access (SQL queries). You can condition access per user on connections, tables, columns (CLS), and rows (RLS). While some concepts also apply to other API operations, the examples here are specific to data queries.
Principles
Section titled “Principles”Reeflow’s security model is built on four principles:
- Deny by default: Users cannot access data unless explicitly permitted. No access is granted implicitly.
- Least privilege: Grant the minimum access needed for each user. Granular scopes (connections, tables, columns, rows) let you define exactly what each user can access.
- Role-based access control (RBAC): Permissions are grouped into roles, which are then assigned to users. This simplifies management and makes it easier to audit who has access to what.
- Policy-level control: Define access rules at the role level, not per workbook or chart. A rule like “only access data from this organization” applies to all data access automatically. This prevents data leaks from configuration mistakes.
Concepts
Section titled “Concepts”Principals
Section titled “Principals”A principal is any entity that accesses Reeflow. Reeflow recognizes three types of principals, each authenticated differently:
- Platform users: Team members who sign in to the Console. Authenticated via Reeflow or your identity provider.
- Embedded users: End users in your application who view embedded analytics. Manage these through the API (recommended) or the Console.
- API keys: Credentials for programmatic access, such as creating session tokens or running scheduled jobs. Support HMAC signatures or Basic authentication.
Embedded users authenticate using session tokens: short-lived JWTs generated by your backend. Because these tokens are exposed to the browser, they expire quickly (typically within minutes) to limit the window of potential misuse.
Roles are the central mechanism for controlling access. A role bundles permissions with user attribute rules, allowing you to define both what a principal can do and what context they must provide. Roles can be assigned to teams, API keys, and embedded users.
For details on role components, permissions, and how to configure them, see Roles.
User attributes
Section titled “User attributes”User attributes are key-value pairs attached to principals. They provide the dynamic context that makes row-level and column-level security possible.
For example, a tenant_id attribute lets you filter data so each customer only sees their own records, without creating separate roles for each customer.
How requests are evaluated
Section titled “How requests are evaluated”When a principal makes a request, Reeflow evaluates it through two stages: authorization and data filtering.
flowchart TD
subgraph Auth [**Authorization**]
B[Resolve roles assigned to principal] --> C[Merge permissions from all roles]
C --> D{Action permitted?}
end
subgraph DF [**Data Filtering**]
F[Merge user attributes from principal and roles] --> G[Restrict tables to allowed list]
G --> H[Restrict columns to allowed list]
H --> I[Inject row filters into query]
I --> J[Return filtered data]
end
D -->|Yes| F
D -->|No| E[Denied]
Authorization
Section titled “Authorization”The first stage determines whether the principal can perform the requested action:
- Resolve roles: Identify which roles the principal can assume. A principal can only assume a role if they provide all of its required user attributes.
- Merge permissions: Combine permissions from all assumable roles. Access is granted if any role permits the action on the target resource.
- Check action: Verify the requested action is permitted. If not, the request is denied.
For details on how roles resolve and permissions merge, see Roles.
Data filtering
Section titled “Data filtering”Once authorization succeeds, the second stage determines what data the principal can see. This only applies to data queries:
- Resolve user attributes: Merge principal user attributes with role fixed user attributes. Fixed values take precedence.
- Apply table scope: Remove any tables not in the allowed list from the query.
- Apply column scope: For each table, restrict visible columns to the allowed list.
- Apply row filters: Inject row-level constraints into the query using resolved user attributes.
After these steps, the query executes against the data source with all filters applied. The principal only sees data they are explicitly permitted to access.
Security levels
Section titled “Security levels”Query permissions control data access at four levels, from broadest to most granular.
| Level | What it controls | When to use | Example |
|---|---|---|---|
| Connection | Which connections can be queried | Separate data sources by use case or environment | Allow production queries only for internal dashboards |
| Table | Which tables can be queried within allowed connections | Restrict access to entire datasets | Prevent external users from querying internal analytics tables |
| Column (CLS) | Which columns are visible within allowed tables | Hide sensitive fields without blocking the table | Hide salary and ssn columns from the employees table |
| Row (RLS) | Which rows are returned, based on dynamic filters | Isolate data by tenant, user, or attribute | Filter orders by tenant_id so each customer sees only their own |
All four levels apply together. A query must target an allowed connection, then an allowed table, then only permitted columns are returned, and finally row filters restrict which records appear.
Error handling
Section titled “Error handling”| Scenario | Result |
|---|---|
| No assumable roles (missing required user attributes) | 403 Forbidden |
| Action not permitted on connection | 403 Forbidden |
| Query references a table not in allowed list | 400 Bad Request |
| Query explicitly selects a column not in allowed list | 400 Bad Request |
| No rows match row-level security filters | Success with empty result set |