Developing a Custom View Policy¶
A custom view policy is a Java class with some annotations that mark the class as a custom view policy and indicate which method the Server has to execute to intercept the query before executing it.
Every time a custom policy is executed, the Server creates a new instance of the class. However, if this Java class has “static” attributes, their value will be the same across executions of the policy.
To develop a custom view policy, add
<DENODO_HOME>/lib/contrib/denodo-commons-custom.jar
to
the classpath of your project.
The Virtual DataPort API Javadoc provides the documentation for the classes and annotations of this API.
There is a sample custom view policy in the directory
<DENODO_HOME>/samples/vdp/customPolicies/
.
This custom view policy limits the number of concurrent queries that a user/role can execute over the same view or stored procedure. This custom view policy has one input parameter called “Limit”, which sets the maximum number of concurrent queries this user/role can execute.
The README
file of this directory explains how to compile the
example and import it into Virtual DataPort.
Once you have developed a custom view policy and imported it into Virtual DataPort, read the article How to debug Denodo custom extensions with Eclipse of the Denodo Knowledge Base to learn how to debug it.
To develop a custom view policy, create a Java class like this one:
import com.denodo.common.custom.annotations.*;
import com.denodo.common.custom.policy.*;
@CustomElement(name = "my_new_custom_policy", type = CustomElementType.VDPCUSTOMPOLICY)
public class MyDenodoCustomPolicy {
@CustomContext
CustomRestrictionPolicyContext context;
@CustomExecutor
public CustomRestrictionPolicyValue execute(
@CustomParam(name = "parameter1", mandatory = false) String inputParameter {
/*
* This custom view policy accepts all the queries that involve
* the view over which this policy was assigned.
*/
return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT);
}
}
The attribute name
of @CustomElement
is the name of the custom view policy.
With the attribute context
, you can obtain information about the context of the query:
Query that Virtual DataPort received:
getQuery()
.Fields involved in the query. That is, the list of fields in the
SELECT
,WHERE
,GROUP BY
andHAVING
clauses:getFieldsInQuery()
.User account that executes the query:
getCurrentUserName()
.List of the effective roles of the user account that executes the query:
getCurrentUserRoles()
. That is, the roles granted directly to the user and the ones granted to the roles of the user, not the user itself.User agent of to the connection that executes the query:
getCurrentUserAgent()
.Database where the query is executed:
getCurrentDatabaseName()
. This may be different from the database to which the view belongs. For example, if you are connected to database “db1” and you executeSELECT * FROM db2.customer
, this method will return “db1”.User / role to whom the custom view policy was assigned:
getPolicyCredentialsName()
andgetPolicyCredentialsType()
. The latter method returns if the policy is assigned to a user or a role.View or stored procedure that the custom view policy was assigned to:
getElementType()
andgetElementName()
.Database of the element that the custom view policy was assigned to:
getElementDatabase()
.Collection with the assigned tags to the element
getElementTags()
.Map with the assigned tags to the fields from the element
getFieldsTags()
.Properties of the query. Invoke
getProperty(...)
to obtain the value of the property andsetProperty(...)
to change it. The available properties are the constants defined in theCustomRestrictionPolicyContext
class:I18N_PROPERTY
,SWAP_PROPERTY
, etc.Extra authentication information, if it was provided by the authentication mechanism:
getAdditionalAuthenticationInformation()
.Provides a JMX connection to the Virtual DataPort server that the custom view policy can use to retrieve any data via JMX:
getJmxConnection()
. The JMX interface of Denodo provides information about the current status of the server. This would allow you to modify the behavior of the policy based on the load of the Denodo server, the number of concurrent queries, etc.To log a message invoke the method
log(...)
. The messages logged using this method are redirected to the log categorycom.denodo.vdb.catalog.view.CustomRestrictionPolicyContextImpl
.To check if a log category is enabled:
isLogLevelEnabled(...)
.For the full list of options, see the Javadoc of the class CustomRestrictionPolicyContext
Note
The custom view policy mechanism initializes the attribute context
right before invoking the method execute
; this attribute will
be null if you try to access it from the constructor of the class.
To add parameters to the custom view policy, add a parameter to the method execute
and annotate it with
@CustomParam(name = "<parameter_name>", mandatory = false)
. This annotation
has two parameters:
name
: the Administration Tool uses this value when displaying information about this custom view policy to the users.mandatory
: boolean value that indicates if this parameter is mandatory or not.
Later, when you assign this policy to a user/role over a view, you will have to provide a value for the input parameters of the policy.
This Java class must have one and only one method annotated with @CustomExecutor
(in this example, this is the execute
method).
When the execution engine loads a custom view policy during the execution of a query, it will execute the method
marked with this annotation, which has to return a CustomRestrictionPolicyValue
object.
To accept a query “as is”, return this:
return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT);
To reject a query, return this:
return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.REJECT);
To add a condition to the query, return this:
String condition = "title <> 'Chief Executive Officer'"; return new CustomRestrictionPolicyValue( CustomRestrictionPolicyType.ACCEPT_WITH_FILTER , CustomRestrictionPolicyFilterType.REJECT_ROW , condition , null);
In this example, the execution engine will add the condition “title <> ‘Chief Executive Officer’” to the query executed by the user.
To add a condition to query when one or more fields of a list of fields are used in the query, return the following. A field is considered to be used in a query if at least one of these clauses uses the field:
SELECT
,WHERE
,GROUP BY
,HAVING
.String condition = "title <> 'Chief Executive Officer'"; Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus")); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.REJECT_ROW_IF_ANY_SENSITIVE_FIELDS_USED , condition , sensitiveFields);
In this example, if the query uses the fields “salary” and/or “bonus”, the condition “title <> ‘Chief Executive Officer’” will be added to the query. If the query does not use any of these fields, the condition will not be added to the query.
To add a condition to the query when all the fields of a list of fields are used in the query, return the following. A field is considered to be used in a query if at least one of these clauses uses the field:
SELECT
,WHERE
,GROUP BY
,HAVING
.String condition = "title <> 'Chief Executive Officer'"; Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus")); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.REJECT_ROW_IF_ALL_SENSITIVE_FIELDS_USED, condition, sensitiveFields);
In this example, if the query uses both fields “salary” and “bonus”, the condition “title <> ‘Chief Executive Officer’” will be added to the query. If the query does not use all of the fields, the condition will not be added to the query.
Note that in this example, we use the constant
REJECT_ROW_IF_ALL_SENSITIVE_FIELDS_USED
and in the previous example, we useREJECT_ROW_IF_ANY_SENSITIVE_FIELDS_USED
.To mask the values of a list of fields in the rows that do not verify a certain condition, when one or more of these fields are used in the query, return the following:
String condition = "title <> 'Chief Executive Officer'"; Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus")); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition, sensitiveFields);
To mask a field, its value is set to
null
.In this example, if the query uses the fields “salary” and/or “bonus”, the fields of the list “sensitiveFields” (“salary” and “bonus”) will be set to NULL in the rows that do not verify the condition. In this example, in the rows whose field “title” is “Chief Executive Officer” (i.e. do not meet the condition), the fields “salary” and “bonus” will be NULL.
If the query does not use any of these fields, no fields will be masked.
To mask the values of a list of fields in the rows that do not verify a condition, when all these fields are used in the query, return the following:
String condition = "title <> 'Chief Executive Officer'"; Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus")); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ALL_USED, condition, sensitiveFields);
To mask a field, its value is set to
null
.In this example, if the query uses the fields “salary” and “bonus”, the fields of the list “sensitiveFields” (“salary” and “bonus”) will be set to NULL in the rows that do not verify the condition. In this example, in the rows whose field “title” is “Chief Executive Officer” (i.e. do not meet the condition), the fields “salary” and “bonus” will be NULL.
If the query does not use all of the fields, the condition will not be added to the query nor any of the fields will be masked.
To mask the values of some fields in all the rows of the result of the query (i.e. with the option
MASK_SENSITIVE_FIELDS_IF_ALL_USED
orMASK_SENSITIVE_FIELDS_IF_ANY_USED
), use a condition that always evaluates to false. For example:1 is null
.String condition = "1 is null"; Set<String> sensitiveFields = new HashSet<>(Arrays.asList("salary", "bonus")); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition, sensitiveFields);
In this example, if the query uses the fields “salary” and/or “bonus”, the fields “salary” and “bonus” will bet set to NULL in all the rows.
To mask values from the previous example with a different masking, use the constructor which receives the default masking expression and a map with the masking configuration per field. For example, for mask “salary” with the default behavior and “bonus” with
0
value.String condition = "1 is null"; Map<String, CustomMaskingExpression> sensitiveFields = new LinkedHashMap<>(); sensitiveFields.put("salary", CustomMaskingExpression.DEFAULT); sensitiveFields.put("bonus", CustomMaskingExpression.SET_0); return new CustomRestrictionPolicyValue(CustomRestrictionPolicyType.ACCEPT_WITH_FILTER, CustomRestrictionPolicyFilterType.MASK_SENSITIVE_FIELDS_IF_ANY_USED, condition, CustomMaskingExpression.REDACT, sensitiveFields);
In this example, if the query uses the fields “salary” and/or “bonus”, the field “salary” will be set to the result of the
REDACT
expression for the column type and “bonus” will bet set to0
in all the rows.To force a limit on the number of rows obtained from a view even if there is an offset/fetch clause, set your desired
QUERY_LIMIT
and then set the booleanFORCE_STRICT_QUERY_LIMIT
to true.this.context.setProperty("QUERY_LIMIT","50"); this.context.setProperty("FORCE_STRICT_QUERY_LIMIT", String.valueOf(true));
In this example, the
QUERY_LIMIT
is set to 50 and theFORCE_STRICT_QUERY_LIMIT
to true. This means that querys won’t return more than the first 50 values. For example, if we make a query with offset 10 and fetch 60, the result will just show 40 rows.
If an input parameter of the custom view policy is a datetime value, check the section Dealing with Datetime and Interval Types for custom functions. The rules to manage input parameters in custom view policies are the same as in custom functions.