Bold BI supports Row-Level Security (RLS) to ensure users only access data they are authorized to see.
RLS can be implemented in two ways:
Row-level security can be enforced by passing an embed_datasource_filter
attribute in the embed token.
This ensures filtering is applied server-side, securely, and without exposing filter logic to end users.
Example: Token Generation with Data Filter
Pass data filters via embed_datasource_filter
in the token generation method to enforce row-level security by filtering data dynamically and use this token in frontend to render the dashboard.
Node
const crypto = require('crypto');
const https = require('https');
const http = require('http');
const url = require('url');
app.post('/tokengeneration', function (req, res) {
const serverUrl = "<Bold BI Server URL>";
const siteIdentifier = "<Bold BI Server Site Identifier>";
const dashboardId = "<Dashboard Id>";
const userEmail = "<User Email>";
const embedSecret = "<Embed Secret Key>";
// Define row-level filter
const filter = "[{shipCountry=India}]";
const serverApiUrl = serverUrl + "/api/" + siteIdentifier;
let queryString = "embed_nonce=" + crypto.randomUUID();
queryString += "&embed_dashboard_id=" + dashboardId;
queryString += "&embed_user_email=" + userEmail;
// Add filter
queryString += "&embed_datasource_filter=" + filter;
// Sign query
const embedSignature = "&embed_signature=" + getSignatureUrl(queryString, embedSecret);
const embedDetailsUrl = "/embed/authorize?" + queryString + embedSignature;
const serverProtocol = url.parse(serverApiUrl).protocol === 'https:' ? https : http;
serverProtocol.get(serverApiUrl + embedDetailsUrl, function (resultContent) {
let str = '';
resultContent.on('data', chunk => str += chunk);
resultContent.on('end', function () {
const resultJson = JSON.parse(str);
if (resultJson?.ApiStatus && resultJson.Data?.access_token) {
res.json({ access_token: resultJson.Data.access_token });
} else {
res.status(500).json({ error: "Failed to generate embed token" });
}
});
});
});
function getSignatureUrl(queryString, embedSecret) {
const hmac = crypto.createHmac('sha256', Buffer.from(embedSecret));
return hmac.update(queryString).digest('base64');
}
Filter Syntax Examples
Scenario | Query |
---|---|
Single Filter | &embed_datasource_filter=[{&Param=Value}] |
Multiple Filters | &embed_datasource_filter=[{&Param1=Value1&Param2=Value2}] |
List Format (IN) | &embed_datasource_filter=[{&Param=IN(Value1,Value2)}] |
Note: Filters must be enclosed in [] square brackets and {} curly braces. You can also achieve RLS for custom columns using dashboard parameters.
Custom Attributes are name–value pairs that can be defined at the user, group, or site level. It can be enforced by passing an embed_custom_attribute
attribute in the embed token.
They allow dashboards to dynamically adjust queries, expressions, or data source connections to enforce RLS.
Learn More About Custom Attribute: For detailed examples of configure and using custom attributes in dashboard, refer to Custom Attribute Usage.
Example: Token Generation with Custom Attribute
In this scenario, two users access the same dashboard with different databases sharing the same schema. We configured a custom attribute with the database name DB1 in the data source connection of the Bold BI server. In embedding, we dynamically assign a different database, DB2, using the embed_custom_attribute parameter, and the dashboard is rendered with the specified database details.
Note: Explore our demo to see how RLS is achieved with custom attributes.
Node
app.post('/tokengeneration', function (req, res) {
const serverUrl = "<Bold BI Server URL>";
const siteIdentifier = "<Site Identifier>";
const dashboardId = "<Dashboard Id>";
const userEmail = "<User Email>";
const embedSecret = "<Embed Secret Key>";
// Define custom attribute
const customAttribute = '[{"database_name":"DB2"}]';
const serverApiUrl = serverUrl + "/api/" + siteIdentifier;
let queryString = "embed_nonce=" + crypto.randomUUID();
queryString += "&embed_dashboard_id=" + dashboardId;
queryString += "&embed_user_email=" + userEmail;
// Add custom attribute
queryString += "&embed_custom_attribute=" + customAttribute;
// Sign query
const embedSignature = "&embed_signature=" + getSignatureUrl(queryString, embedSecret);
const embedDetailsUrl = "/embed/authorize?" + queryString + embedSignature;
const serverProtocol = url.parse(serverApiUrl).protocol === 'https:' ? https : http;
serverProtocol.get(serverApiUrl + embedDetailsUrl, function (resultContent) {
let str = '';
resultContent.on('data', chunk => str += chunk);
resultContent.on('end', function () {
const resultJson = JSON.parse(str);
if (resultJson?.ApiStatus && resultJson.Data?.access_token) {
res.json({ access_token: resultJson.Data.access_token });
} else {
res.status(500).json({ error: "Failed to generate embed token" });
}
});
});
});
function getSignatureUrl(queryString, embedSecret) {
const hmac = crypto.createHmac('sha256', Buffer.from(embedSecret));
return hmac.update(queryString).digest('base64');
}
Examples:Custom Attribute Syntax
Scenario | Syntax | Example |
---|---|---|
Single Attribute | [{"Attribute_Name":"Value"}] |
&embed_custom_attribute='[{"database_name":"DB1"}]' |
Multiple Attributes | [{"Attr1":"Value1","Attr2":"Value2"}] |
&embed_custom_attribute='[{"department":"IT","name":"David"}]' |
List Format (IN) | [{"Attribute_Name":"IN('Value1','Value2')"}] |
&embed_custom_attribute='[{"department":"IN('CSE','EEE')"}]' |
Scenario | Use Data Filter | Use Custom Attribute | Recommendation |
---|---|---|---|
User-Specific Data Filtering | If you need to filter data for individual users based on static or dynamic criteria (e.g., user location, department), Data Filters are the best choice. | Custom Attributes are more useful when tailoring data by user attributes (e.g., role, tier). Enables flexible, dynamic control. | Use Data Filters if criteria map directly to fields. Use Custom Attributes if filtering depends on user identity metadata. |
Role-Based Access Control (RBAC) | Works for simple, fixed role sets (e.g., Admin, User). Harder to scale for many roles or complex orgs. | Great for dynamic role-based filtering. User attributes (roles, departments, groups) drive access automatically. | Custom Attributes are better for scalable RBAC. Data Filters only fit basic role checks. |
Complex User Groups | Effective if groups can be predefined and filter values remain static. | Handles dynamic, overlapping, or nested groups (e.g., contractors, multi-tenant groupings). | Custom Attributes are more flexible unless groups are small and static. |
Multi-Tenant Scenarios | Requires manual filter management for each tenant, which is hard to scale. | Ideal in multi-tenant environments where each tenant may have a different data or access control. Easily pass different values for each tenant. | Custom Attributes are the clear choice for clean, scalable isolation. |
Easier Implementation | Straightforward for simple rules like filtering by department or country. Minimal setup required. | Requires attribute infrastructure and more backend logic. | Start with Data Filters for a quick prototype; move to Custom Attributes as complexity grows. |