Developing a Custom Policy¶
A custom policy is a Java class with some annotations that mark the class as a custom 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.
We strongly recommend using the Denodo4E plugin for Eclipse, to develop
custom policies (see the file README
in
<DENODO_HOME>/tools/denodo4e
).
To develop a custom policy, add the
<DENODO_HOME>/lib/contrib/denodo-commons-custom.jar
file to
the Classpath of your environment.
The Virtual DataPort API Javadoc provides the documentation for the classes and annotations of this API.
There is a sample custom policy in the directory
<DENODO_HOME>/samples/vdp/customPolicies/
This custom policy limits the number of concurrent queries that a user/role can execute over the same view or stored procedure. This custom 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.
To develop a custom 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 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 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 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 policy was assigned to:
getElementType()
andgetElementName()
.Database of the element that the custom policy was assigned to:
getElementDatabase()
.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.Provides a JMX connection to the Virtual DataPort server that the custom 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 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 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 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 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.
If an input parameter of the custom 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 policies are the same as in custom functions.