Fine-Grained Privileges at View Level¶
Fine-grained restrictions in Denodo are a powerful security mechanism that allows a great level of flexibility and control over the data you want to expose to other consumers. Denodo allows different kind of fine-grained access control over a view:
Column privileges: limit the columns a user/role can use in a query.
Row privileges: allow different actions over subsets of rows:
Reject rows
Reject rows if some sensitive fields are used
Mask sensitive fields
In case you scenario has specific requirements that require an Ad hoc implementation, Denodo also provides an API to create your own custom policies.
In order to understand the use of fine-grained privileges it is important to bear in mind that giving access to a role over a specific view is not only giving access to execute that view. If that role has enough privileges to create views, it is also allowing that role to create new views on top of it and decide who can have access to those new views. This means that giving access to a user to execute a view is also transferring the ability to grant privileges to other users over that data.
For example, let’s say you want to allow developers from the HR department to access a canonical view EMPLOYEE containing data regarding employees. At the same time, you do not want other departments like sales to have access to that data as it contains some personal information about the employees. As those developers from the HR department have access to EMPLOYEE, they cannot only execute queries using that view and create new views using the employee information. For example, they could decide to allow the sales department access the new views they have created on top of EMPLOYEE and therefore indirectly accessing data from the EMPLOYEE view.
This is when fine-grained restrictions at intermediate levels come into play:
If you specify a fine-grained restriction on a view for a specific role, that restriction is always applied even if the user with that role executes a view built on top of it (unless the user has another role with more privileges).
So, following the same example, you could enforce a restriction over EMPLOYEE so only people from the HR department could access the data. That way, the HR department can still grant access to the sales department to execute their views, but they will not be able to see any of the employee information as the restriction will always apply over the EMPLOYEE view.
Note
Giving execute access over a view to a user with CREATE privilege is also indirectly transferring the ability to grant privileges to third users over that data. Fine-grained restrictions allow us to set limits on this privilege, ensuring certain security rules are always applied.
The restriction enforcement is very versatile as different users can see different versions of the same dataset without needing to replicate the same restrictions on every view. This can make someone think it is a good idea to define these restrictions on intermediate views with the purpose of reusing the same restriction on different views. However, this is not a good practice in general as it can make their management harder. It is a better approach to always define the restrictions on those views acting as facades on the security borders. Additionally, you can make use of tag-based security policies in order to reuse many of the restrictions’ definitions.
Note
As a general rule, a data owner that wants to expose a view to other users should define the fine-grained restrictions on that view that is visible to those users and not on the views under it.
Alternatively, if you want to avoid that giving access to a user to execute a view is also transferring the ability to grant privileges to other users over that data, you would have the following options:
Use tag-based security policies to restrict access of a view for users of specific roles.
Use the INDIRECT_ACCESS privilege to decide which roles will have access to the data of the view.
Column Privileges¶
When you grant the EXECUTE privilege to a user/role over a view, this user or the users with this role can:
Execute a SELECT statement to query this view
If the user/role also has the INSERT/UPDATE/DELETE privileges over a view, they can execute INSERT/UPDATE/DELETE statements over this view.
In some scenarios, you may want to limit the access to certain columns of a view to users. The columns of a view that cannot be queried or modified through INSERT/UPDATE/DELETE statements are called protected columns.
For example, let us say you have a view “employee”. You may want to forbid users with the role “developer” to see the column “salary”. That is, you want “salary” to be a protected column for the users of the role “developer”. To do this, follow these steps:
Click the menu Administration > Role Management. In the tables with roles, select the role “developer”.
Click Assign privileges and on the row of the database of the view “employee”, click edit.
Select, at least, the privilege “Execute” over this view.
Select the row of the view “employee” and click Assign column privileges.
In this dialog, clear the check box next to the field “salary”.
With this change, the user will be able to run this query:
SELECT ename
FROM employee
However, this one will fail:
SELECT ename, salary
FROM employee
If instead of making the query fail, you want to mask the values of a column, you can specify a row restriction with column masking (see section Row Restrictions below).
When assigning column privileges, consider this:
If you assign column privileges to a user over a view, the statement executed by this user will fail if the query references a protected column of this view in any of the clauses of the query (SELECT, WHERE, GROUP BY, etc.). This is to ensure that users cannot guess the value of protected columns by for example, using them in the WHERE clause. Find an example of this in the next section.
Column privileges do not affect global administrators or administrators of the database to which the view belongs. They can reference protected columns in their statements.
The Execution Engine takes into account the column privileges, row restrictions and custom policies granted over a view to a user/role, regardless of if the view with the column privilege is directly referenced in the query or not.
The following table lists the behavior of column privileges for each DML statement:
Statement |
Behavior |
|
---|---|---|
SELECT |
Does not reference protected columns |
Query runs |
References protected columns |
Query fails |
|
CREATE MATERIALIZED TABLE <materialized table> AS SELECT … FROM <view with restricted columns> |
Does not reference protected columns |
Query runs |
References protected columns |
Query fails |
|
INSERT |
Statement always runs |
|
INSERT INTO <materialized view> SELECT … FROM <view with restricted columns> |
Does not reference protected columns |
Statement runs |
References protected columns |
Statement fails |
|
UPDATE |
Does not reference protected columns |
Statement runs |
References protected columns |
Statement fails |
|
DELETE |
Does not reference protected columns |
Statement runs |
References protected columns |
Statement fails |
Row Restrictions¶
When a user/role has the EXECUTE/UPDATE/DELETE privilege over a view, you can define a restriction over that view to restrict the rows returned to that user/role or affected by the UPDATE/DELETE statements. That way, when that user executes a DML operation over the view, they will only obtain/update/delete the rows that match a certain condition and/or some of the fields of some rows will be masked.
For example, let us say you want to restrict the access to the view “employee” to users with role “developer”. To assign a row restriction to a user/role: edit that user/role, select the view you want to restrict (“employee” in our example), click Assign restrictions and then click New restriction.
In this dialog, you first need to specify a condition. The user will be able to see the rows that do meet the condition you enter (e.g. position <> 'manager'
). For the rows that do not meet the condition, you have to select one of the following actions:
Reject row. In our example, the user will only see/update/delete the employees who are not managers.
Reject row if any or all sensitive fields are used (you can select which ones). For example, you could allow the user to see all the employees’ data if the statement does not reference use the column “salary”. If the select/update/delete operation references the column “salary” then the statement will only affect the employees that are not managers.
Mask sensitive fields if any or all of them are used (you can select which ones). For example, you could allow the user to see all the employees’ data, but for the ones who are managers, the values of the field salary will be replaced by a masking expression.
The following table shows the masking expressions to replace sensitive values.
Expression |
Description |
---|---|
Hide |
A |
Show first 4 characters |
Only first four characters are returned with a fixed number of asterisks. |
Show latest 4 characters |
Only latest four characters are returned with a fixed number of asterisks. |
Only year |
Only returns the real year. Other values are reset to default. |
Redact |
Returns a predefined masking value depending on the field type. |
Redact with asterisk |
Returns a fixed number of asterisks. |
Remove time |
Reset the time values to default. |
Round |
Rounds the number value. |
Set to 0 |
Returns a |
Set to -1 |
Returns a |
Custom |
A user defined expression. Note that the expression must have the
same type than the field. A |
The following table shows the REDACT default expressions per type.
Type |
Expression |
---|---|
Numbers |
Returns a |
Text |
Returns a fixed number of asterisks. |
Date times |
Returns the correct type (localdate, timestamp…) with the values reset to default. For example, for ‘localdate’ the value of 1970-01-01 and for ‘timestamp’ the value of 1970-01-01 00:00:00 |
Other types |
A |
You can develop what is called a custom policy that takes into account any information you want to decide if the result of the query is filtered or not. The section Custom Policies of the Developer Guide explains what they are and how to develop them.
When assigning row restrictions, consider this:
The row restrictions do not affect global administrators or administrators of the database to which the view belongs. I.e. no row will be rejected or masked regardless for them.
The Execution Engine takes into account the column privileges, row restrictions and custom policies granted over a view to a user/role, regardless of if the view with the column privilege is directly referenced in the query or not.
The option Reject row if any/all sensitive fields are used and the masking are applied when the query references a sensitive field of this view from any of the clauses of the query (SELECT, WHERE, GROUP BY, etc.). This is to avoid users guessing the value of a sensitive field by using it from the WHERE clause. For example, if the sensitive field salary was only masked on the SELECT clause, the user could try to guess the values using queries like:
SELECT ename FROM employee WHERE salary > 50000 and salary < 100000;
or
UPDATE employee SET ename = ename || '_100000' WHERE salary > 100000
The following table shows the behavior of “Reject Row” restrictions, for each type of DML statement:
Statement |
Behavior |
---|---|
SELECT |
Virtual DataPort executes the DML command including the condition from the restriction |
CREATE MATERIALIZED TABLE <materialized table> AS SELECT … FROM <view with restrictions> |
Virtual DataPort executes the DML command including the condition from the restriction |
INSERT |
No restrictions |
INSERT INTO <materialized table> SELECT … FROM <view with restrictions> |
Virtual DataPort executes the DML command including the condition from the restriction |
UPDATE |
Virtual DataPort executes the DML command including the condition from the restriction |
DELETE |
Virtual DataPort executes the DML command including the condition from the restriction |
For example, let us say you assign a row restriction over the view “employee” to the role “sales_manager”.
This row restriction rejects rows that do not verify the condition department = 'sales'
.
If a user with role “sales_manager” executes:
SELECT * FROM employee
SELECT * FROM employee WHERE department = 'sales'
If the user has the UPDATE privilege over employee and executes:
UPDATE employee SET manager_id = 1 WHERE manager_id = 2
UPDATE employee SET manager_id = 1 WHERE manager_id = 2 AND department = 'sales'
The following table shows the behavior of restrictions “Reject Row if sensitive fields are used”, for each type of DML statement:
Statement |
Behavior |
|
---|---|---|
SELECT |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort executes the DML command including the condition from the restriction |
|
CREATE MATERIALIZED TABLE <materialized table> AS SELECT … FROM <restricted view> |
Not using sensitive fields |
No restrictions |
Using sensitive fields in SELECT query |
Virtual DataPort executes the DML command including the condition from the restriction |
|
INSERT |
No restrictions |
|
INSERT INTO <materialized table> SELECT … FROM <view with restrictions> |
Not using sensitive fields |
No restrictions |
Using sensitive fields in SELECT query |
Virtual DataPort executes the DML command including the condition from the restriction |
|
UPDATE |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort executes the DML command including the condition from the restriction |
|
DELETE |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort executes the DML command including the condition from the restriction |
For example, let us say you assign a row restriction over the view “employee” to the role “developer”.
This row restriction rejects rows that do not verify the condition position <> 'manager'
, but only if the statement uses
the sensitive column salary.
If a user with role “developer” executes:
SELECT ename FROM employee
SELECT ename FROM employee WHERE salary > 50000
SELECT ename FROM employee WHERE salary > 50000 and position <> 'manager'
The same way, if the same user executes:
CREATE MATERIALIZED TABLE employee_salary AS SELECT ename, salary FROM employee
SELECT ename, salary FROM employee WHERE position <> 'manager'
The following table show the behavior of restrictions “Mask sensitive fields if ANY/ALL of them are used”, for each type of statement:
DML Operation |
Behavior |
|
---|---|---|
SELECT |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort masks the value for the sensitive columns |
|
CREATE MATERIALIZED TABLE <materialized table> AS SELECT * FROM <view with restrictions> |
Not using sensitive fields |
No restrictions |
Using sensitive fields in SELECT query |
Virtual DataPort masks the value for the sensitive columns |
|
INSERT |
No restrictions |
|
INSERT INTO <materialized table> SELECT * FROM <view with restrictions> |
Not using sensitive fields |
No restrictions |
Using sensitive fields in SELECT query |
Virtual DataPort masks the value for the sensitive columns |
|
UPDATE |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort executes the DML command including the condition from the restriction |
|
DELETE |
Not using sensitive fields |
No restrictions |
Using sensitive fields |
Virtual DataPort executes the DML command including the condition from the restriction |
Using the previous example, if a user with role “developer” executes:
SELECT ename, salary FROM employee
It will return all rows, but for those rows corresponding to managers, the values of salary will be masked.
The same way, if the same user executes the query:
SELECT ename FROM employee WHERE salary > 50000
The user will see the result of the following query:
SELECT ename FROM employee WHERE CASE WHEN (position <> 'manager') THEN salary ELSE null END > 50000
This is, the names for the employees whose position is not manager.
If the same user has the privilege DELETE over employee and executes:
DELETE FROM employee
It will delete all employees. However, if the user executes:
DELETE FROM employee WHERE salary > 50000
It will only delete the rows WHERE salary > 50000 and position is not ‘manager’