This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Access policies
CREATE ACCESS POLICY lets you create access policies on tables that specify how much data certain users and roles can query from those tables.
CREATE ACCESS POLICY lets you create access policies on tables that specify how much data certain users and roles can query from those tables. Access policies typically prevent these users from viewing the data of specific columns and rows of a table. You can apply access policies to table columns and rows. If a table has access policies on both, Vertica filters row access policies first, then filters the column access policies.
You can create most access policies for any table type—columnar, external, or flex. (You cannot create column access policies on flex tables.) You can also create access policies on any column type, including joins.
1 - Creating column access policies
CREATE ACCESS POLICY can create access policies on individual table columns, one policy per column.
CREATE ACCESS POLICY can create access policies on individual table columns, one policy per column. Each column access policy lets you specify, for different users and roles, various levels of access to the data of that column. The column access expression can also specify how to render column data for users and roles.
The following example creates an access policy on the customer_address
column in the client_dimension
table. This access policy gives non-superusers with the administrator
role full access to all data in that column, but masks customer address data from all other users:
=> CREATE ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address
-> CASE
-> WHEN ENABLED_ROLE('administrator') THEN customer_address
-> ELSE '**************'
-> END ENABLE;
CREATE ACCESS POLICY
Note
Vertica roles are compatible with LDAP users. You do not need separate LDAP roles to use column access policies with LDAP users.
Vertica uses this policy to determine the access it gives to users MaxineT and MikeL, who are assigned employee
and administrator
roles, respectively. When these users query the customer_dimension
table, Vertica applies the column access policy expression as follows:
=> \c - MaxineT;
You are now connected as user "MaxineT".
=> SET ROLE employee;
SET
=> SELECT customer_type, customer_name, customer_gender, customer_address, customer_city FROM customer_dimension;
customer_type | customer_name | customer_gender | customer_address | customer_city
---------------+-------------------------+-----------------+------------------+------------------
Individual | Craig S. Robinson | Male | ************** | Fayetteville
Individual | Mark M. Kramer | Male | ************** | Joliet
Individual | Barbara S. Farmer | Female | ************** | Alexandria
Individual | Julie S. McNulty | Female | ************** | Grand Prairie
...
=> \c - MikeL
You are now connected as user "MikeL".
=> SET ROLE administrator;
SET
=> SELECT customer_type, customer_name, customer_gender, customer_address, customer_city FROM customer_dimension;
customer_type | customer_name | customer_gender | customer_address | customer_city
---------------+-------------------------+-----------------+------------------+------------------
Individual | Craig S. Robinson | Male | 138 Alden Ave | Fayetteville
Individual | Mark M. Kramer | Male | 311 Green St | Joliet
Individual | Barbara S. Farmer | Female | 256 Cherry St | Alexandria
Individual | Julie S. McNulty | Female | 459 Essex St | Grand Prairie
...
Restrictions
The following limitations apply to access policies:
-
A column can have only one access policy.
-
Column access policies cannot be set on columns of complex types other than native arrays.
-
Column access policies cannot be set for materialized columns on flex tables. While it is possible to set an access policy for the __raw__
column, doing so restricts access to the whole table.
-
Row access policies are invalid on temporary tables and tables with aggregate projections.
-
Access policy expressions cannot contain:
-
If the query optimizer cannot replace a deterministic expression that involves only constants with their computed values, it blocks all DML operations such as INSERT.
2 - Creating row access policies
CREATE ACCESS POLICY can create a single row access policy for a given table.
CREATE ACCESS POLICY can create a single row access policy for a given table. This policy lets you specify for different users and roles various levels of access to table row data. When a user launches a query, Vertica evaluates the access policy's WHERE expression against all table rows. The query returns with only those rows where the expression evaluates to true for the current user or role.
For example, you might want to specify different levels of access to table store.store_store_sales
for four roles:
-
employee
: Users with this role should only access sales records that identify them as the employee, in column employee_key
. The following query shows how many sales records (in store.store_sales_fact
) are associated with each user (in public.emp_dimension
):
=> SELECT COUNT(sf.employee_key) AS 'Total Sales', sf.employee_key, ed.user_name FROM store.store_sales_fact sf
JOIN emp_dimension ed ON sf.employee_key=ed.employee_key
WHERE ed.job_title='Sales Associate' GROUP BY sf.employee_key, ed.user_name ORDER BY sf.employee_key
Total Sales | employee_key | user_name
-------------+--------------+-------------
533 | 111 | LucasLC
442 | 124 | JohnSN
487 | 127 | SamNS
477 | 132 | MeghanMD
545 | 140 | HaroldON
...
563 | 1991 | MidoriMG
367 | 1993 | ThomZM
(318 rows)
-
regional_manager
: Users with this role (public.emp_dimension
) should only access sales records for the sales region that they manage (store.store_dimension
):
=> SELECT distinct sd.store_region, ed.user_name, ed.employee_key, ed.job_title FROM store.store_dimension sd
JOIN emp_dimension ed ON sd.store_region=ed.employee_region WHERE ed.job_title = 'Regional Manager';
store_region | user_name | employee_key | job_title
--------------+-----------+--------------+------------------
West | JamesGD | 1070 | Regional Manager
South | SharonDM | 1710 | Regional Manager
East | BenOV | 593 | Regional Manager
MidWest | LilyCP | 611 | Regional Manager
NorthWest | CarlaTG | 1058 | Regional Manager
SouthWest | MarcusNK | 150 | Regional Manager
(6 rows)
-
dbadmin
and administrator
: Users with these roles have unlimited access to all table data.
Given these users and the data associated with them, you can create a row access policy on store.store_store_sales
that looks like this:
CREATE ACCESS POLICY ON store.store_sales_fact FOR ROWS WHERE
(ENABLED_ROLE('employee')) AND (store.store_sales_fact.employee_key IN
(SELECT employee_key FROM public.emp_dimension WHERE user_name=CURRENT_USER()))
OR
(ENABLED_ROLE('regional_manager')) AND (store.store_sales_fact.store_key IN
(SELECT sd.store_key FROM store.store_dimension sd
JOIN emp_dimension ed ON sd.store_region=ed.employee_region WHERE ed.user_name = CURRENT_USER()))
OR ENABLED_ROLE('dbadmin')
OR ENABLED_ROLE ('administrator')
ENABLE;
Important
In this example, the row policy limits access to a set of roles that are explicitly included in policy's WHERE expression. All other roles and users are implicitly denied access to the table data.
The following examples indicate the different levels of access that are available to users with the specified roles:
-
dbadmin
has access to all rows in store.store_sales_fact
:
=> \c
You are now connected as user "dbadmin".
=> SELECT count(*) FROM store.store_sales_fact;
count
---------
5000000
(1 row)
-
User LilyCP
has the role of regional_manager
, so she can access all sales data of the Midwest region that she manages:
=> \c - LilyCP;
You are now connected as user "LilyCP".
=> SET ROLE regional_manager;
SET
=> SELECT count(*) FROM store.store_sales_fact;
count
--------
782272
(1 row)
-
User SamRJ
has the role of employee
, so he can access only the sales data that he is associated with:
=> \c - SamRJ;
You are now connected as user "SamRJ".
=> SET ROLE employee;
SET
=> SELECT count(*) FROM store.store_sales_fact;
count
-------
417
(1 row)
Restrictions
The following limitations apply to row access policies:
-
A table can have only one row access policy.
-
Row access policies are invalid on the following tables:
-
You cannot create directed queries on a table with a row access policy.
3 - Access policies and DML operations
By default, Vertica abides by a rule that a user can only edit what they can see.
By default, Vertica abides by a rule that a user can only edit what they can see. That is, you must be able to view all rows and columns in the table in their original values (as stored in the table) and in their originally defined data types to perform actions that modify data on a table. For example, if a column is defined as VARCHAR(9) and an access policy on that column specifies the same column as VARCHAR(10), users using the access policy will be unable to perform the following operations:
-
INSERT
-
UPDATE
-
DELETE
-
MERGE
-
COPY
You can override this behavior by specifying GRANT TRUSTED in a new or existing access policy. This option forces the access policy to defer entirely to explicit GRANT statements when assessing whether a user can perform the above operations.
You can view existing access policies with the ACCESS_POLICY system table.
Row access
On tables where a row access policy is enabled, you can only perform DML operations when the condition in the row access policy evaluates to TRUE. For example:
t1 appears as follows:
A | B
---+---
1 | 1
2 | 2
3 | 3
Create the following row access policy on t1:
=> CREATE ACCESS POLICY ON t1 for ROWS
WHERE enabled_role('manager')
OR
A<2
ENABLE;
With this policy enabled, the following behavior exists for users who want to perform DML operations:
-
A user with the manager role can perform DML on all rows in the table, because the WHERE clause in the policy evaluates to TRUE.
-
Users with non-manager roles can only perform a SELECT to return data in column A that has a value of less than two. If the access policy has to read the data in the table to confirm a condition, it does not allow DML operations.
Column access
On tables where a column access policy is enabled, you can perform DML operations if you can view the entire column in its originally defined type.
Suppose table t1 is created with the following data types and values:
=> CREATE TABLE t1 (A int, B int);
=> INSERT INTO t1 VALUES (1,2);
=> SELECT * FROM t1;
A | B
---+---
1 | 2
(1 row)
Suppose the following access policy is created, which coerces the data type of column A from INT to VARCHAR(20) at execution time.
=> CREATE ACCESS POLICY on t1 FOR column A A::VARCHAR(20) ENABLE;
Column "A" is of type int but expression in Access Policy is of type varchar(20). It will be coerced at execution time
In this case, u1 can view column A in its entirety, but because the active access policy doesn't specify column A's original data type, u1 cannot perform DML operations on column A.
=> \c - u1
You are now connected as user "u1".
=> SELECT A FROM t1;
A
---
1
(1 row)
=> INSERT INTO t1 VALUES (3);
ERROR 6538: Unable to INSERT: "Access denied due to active access policy on table "t1" for column "A""
Overriding default behavior with GRANT TRUSTED
Specifying GRANT TRUSTED in an access policy overrides the default behavior ("users can only edit what they can see") and instructs the access policy to defer entirely to explicit GRANT statements when assessing whether a user can perform a DML operation.
Important
GRANT TRUSTED can allow users to make changes to tables even if the data is obscured by the access policy. In these cases, certain users who are constrained by the access policy but also have GRANTs on the table can make changes to the data that they cannot view or verify.
GRANT TRUSTED is useful in cases where the form the data is stored in doesn't match its semantically "true" form.
For example, when integrating with Voltage SecureData, a common use case is storing encrypted data with VoltageSecureProtect, where decryption is left to a case expression in an access policy that calls VoltageSecureAccess. In this case, while the decrypted form is intuitively understood to be the data's "true" form, it's still stored in the table in its encrypted form; users who can view the decrypted data wouldn't see the data as it was stored and therefore wouldn't be able to perform DML operations. You can use GRANT TRUSTED to override this behavior and allow users to perform these operations if they have the grants.
In this example, the customer_info table contains columns for the customer first and last name and SSN. SSNs are sensitive and access to it should be controlled, so it is encrypted with VoltageSecureProtect as it is inserted into the table:
=> CREATE TABLE customer_info(first_name VARCHAR, last_name VARCHAR, ssn VARCHAR);
=> INSERT INTO customer_info SELECT 'Alice', 'Smith', VoltageSecureProtect('998-42-4910' USING PARAMETERS format='ssn');
=> INSERT INTO customer_info SELECT 'Robert', 'Eve', VoltageSecureProtect('899-28-1303' USING PARAMETERS format='ssn');
=> SELECT * FROM customer_info;
first_name | last_name | ssn
------------+-----------+-------------
Alice | Smith | 967-63-8030
Robert | Eve | 486-41-3371
(2 rows)
In this system, the role "trusted_ssn" identifies privileged users for which Vertica will decrypt the values of the "ssn" column with VoltageSecureAccess. To allow these privileged users to perform DML operations for which they have grants, you might use the following access policy:
=> CREATE ACCESS POLICY ON customer_info FOR COLUMN ssn
CASE WHEN enabled_role('trusted_ssn') THEN VoltageSecureAccess(ssn USING PARAMETERS format='ssn')
ELSE ssn END
GRANT TRUSTED
ENABLE;
Again, note that GRANT TRUSTED allows all users with GRANTs on the table to perform the specified operations, including users without the "trusted_ssn" role.
4 - Access policies and query optimization
Access policies affect the projection designs that the Vertica Database Designer produces, and the plans that the optimizer creates for query execution.
Access policies affect the projection designs that the Vertica Database Designer produces, and the plans that the optimizer creates for query execution.
Projection designs
When Database Designer creates projections for a given table, it takes into account access policies that apply to the current user. The set of projections that Database Designer produces for the table are optimized for that user's access privileges, and other users with similar access privileges. However, these projections might be less than optimal for users with different access privileges. These differences might have some effect on how efficiently Vertica processes queries for the second group of users. When you evaluate projection designs for a table, choose a design that optimizes access for all authorized users.
Query rewrite
The Vertica optimizer enforces access policies by rewriting user queries in its query plan, which can affect query performance. For example, the clients table has row and column access policies, both enabled. When a user queries this table, the query optimizer produces a plan that rewrites the query so it includes both policies:
=> SELECT * FROM clients;
The query optimizer produces a query plan that rewrites the query as follows:
SELECT * FROM (
SELECT custID, password, CASE WHEN enabled_role('manager') THEN SSN ELSE substr(SSN, 8, 4) END AS SSN FROM clients
WHERE enabled_role('broker') AND
clients.clientID IN (SELECT brokers.clientID FROM brokers WHERE broker_name = CURRENT_USER())
) clients;
5 - Managing access policies
By default, you can only manage access policies on tables that you own.
By default, you can only manage access policies on tables that you own. You can optionally restrict access policy management to superusers with the AccessPolicyManagementSuperuserOnly parameter (false by default):
=> ALTER DATABASE DEFAULT SET PARAMETER AccessPolicyManagementSuperuserOnly = 1;
ALTER DATABASE
You can view and manage access policies for tables in several ways:
Viewing access policies
You can view access policies in two ways:
-
Query system table ACCESS_POLICY. For example, the following query returns all access policies on table public.customer_dimension
:
=> \x
=> SELECT policy_type, is_policy_enabled, table_name, column_name, expression FROM access_policy WHERE table_name = 'public.customer_dimension';
-[ RECORD 1 ]-----+----------------------------------------------------------------------------------------
policy_type | Column Policy
is_policy_enabled | Enabled
table_name | public.customer_dimension
column_name | customer_address
expression | CASE WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END
-
Export table DDL from the database catalog with EXPORT_TABLES, EXPORT_OBJECTS, or EXPORT_CATALOG. For example:
=> SELECT export_tables('','customer_dimension');
export_tables
-----------------------------------------------------------------------------
CREATE TABLE public.customer_dimension
(
customer_key int NOT NULL,
customer_type varchar(16),
customer_name varchar(256),
customer_gender varchar(8),
...
CONSTRAINT C_PRIMARY PRIMARY KEY (customer_key) DISABLED
);
CREATE ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address CASE WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END ENABLE;
Modifying access policy expression
ALTER ACCESS POLICY can modify the expression of an existing access policy. For example, you can modify the access policy in the earlier example by extending access to the dbadmin role:
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address
CASE WHEN enabled_role('dbadmin') THEN customer_address
WHEN enabled_role('administrator') THEN customer_address
ELSE '**************' END ENABLE;
ALTER ACCESS POLICY
Querying system table ACCESS_POLICY confirms this change:
=> SELECT policy_type, is_policy_enabled, table_name, column_name, expression FROM access_policy
WHERE table_name = 'public.customer_dimension' AND column_name='customer_address';
-[ RECORD 1 ]-----+-------------------------------------------------------------------------------------------------------------------------------------------
policy_type | Column Policy
is_policy_enabled | Enabled
table_name | public.customer_dimension
column_name | customer_address
expression | CASE WHEN enabled_role('dbadmin') THEN customer_address WHEN enabled_role('administrator') THEN customer_address ELSE '**************' END
Enabling and disabling access policies
Owners of a table can enable and disable its row and column access policies.
Row access policies
You enable and disable row access policies on a table:
ALTER ACCESS POLICY ON [schema.]table FOR ROWS { ENABLE | DISABLE }
The following examples disable and then re-enable the row access policy on table customer_dimension
:
=> ALTER ACCESS POLICY ON customer_dimension FOR ROWS DISABLE;
ALTER ACCESS POLICY
=> ALTER ACCESS POLICY ON customer_dimension FOR ROWS ENABLE;
ALTER ACCESS POLICY
Column access policies
You enable and disable access policies on a table column as follows:
ALTER ACCESS POLICY ON [schema.]table FOR COLUMN column { ENABLE | DISABLE }
The following examples disable and then re-enable the same column access policy on customer_dimension.customer_address
:
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address DISABLE;
ALTER ACCESS POLICY
=> ALTER ACCESS POLICY ON public.customer_dimension FOR COLUMN customer_address ENABLE;
ALTER ACCESS POLICY
Copying access polices
You copy access policies from one table to another as follows. Non-superusers must have ownership of both the source and destination tables:
ALTER ACCESS POLICY ON [schema.]table { FOR COLUMN column | FOR ROWS } COPY TO TABLE table
When you create a copy of a table or move its contents with the following functions (but not CREATE TABLE AS SELECT or CREATE TABLE LIKE), the access policies of the original table are copied to the new/destination table:
To copy access policies to another table, use ALTER ACCESS POLICY.
Note
If you rename a table with
ALTER TABLE...RENAME TO, the access policies that were stored under the previous name are stored under the table's new name.
For example, you can copy a row access policy as follows:
=> ALTER ACCESS POLICY ON public.emp_dimension FOR ROWS COPY TO TABLE public.regional_managers_dimension;
The following statement copies the access policy on column employee_key
from table public.emp_dimension
to store.store_sales_fact
:
=> ALTER ACCESS POLICY ON public.emp_dimension FOR COLUMN employee_key COPY TO TABLE store.store_sales_fact;
Note
The copied policy retains the source policy's enabled/disabled settings.