Nested structure¶
The nested structure authorizer applies Ranger policies to JSON documents — for example the response of a microservice API. Given a schema name, the calling user and a JSON record, it tells the application whether the user may read (or write) the record and returns a copy of the JSON with unauthorized fields masked according to Ranger masking policies. It can also drop whole records with a JavaScript record filter.
It is not tied to a particular server: it is a Java library (ranger-nestedstructure-plugin, module
plugin-nestedstructure) that any JVM application embeds. The enforcement point is your application's call
to NestedStructureAuthorizer, which wraps Ranger's plugin runtime (RangerBasePlugin) and evaluates
access, masking and row-filter policies per field. Policies are pulled from Ranger Admin and cached.
Requirements¶
- A reachable Ranger Admin with a service of type
nestedstructure. - An audit store if auditing is enabled.
- A JVM application. The library depends on
ranger-plugins-common, Gson andjson-path; record filters run on ajavax.scriptJavaScript engine (GraalJS preferred, Nashorn as fallback). - The library jar from the Ranger build (
plugin-nestedstructure/target/ranger-nestedstructure-plugin-<version>.jar, see Build) and its dependencies on the application classpath.
Configuration¶
There is nothing to switch on in a host component: your application calls the authorizer directly.
String schema = "json_object.cxt.cmt.product.vnull3";
String userName = "beckma200";
Set<String> userGroups = new HashSet<>();
String jsonString = ...;
AccessResult result = NestedStructureAuthorizer.getInstance()
.authorize(schema, userName, userGroups, jsonString, NestedStructureAccessType.READ);
String authorizedJson = result.hasAccess() ? result.getJson() : null;
An example client is in plugin-nestedstructure/src/test/java/.../ExampleClient.java.
The library reads the three files below from the application classpath. Templates are in
plugin-nestedstructure/conf; the
template names the service privacera_nestedstructure, uses /tmp as policy cache directory and enables
the Solr audit destination.
ranger-nestedstructure-security.xml¶
This file tells the plugin which Ranger Admin to contact and which service's policies to enforce. Place it
on the application classpath. ranger.plugin.nestedstructure.policy.rest.url and
ranger.plugin.nestedstructure.service.name are mandatory; every other property is shown with its default.
<configuration>
<!-- Connection to Ranger Admin -->
<property>
<name>ranger.plugin.nestedstructure.policy.rest.url</name>
<value>http://ranger-admin:6080</value>
<description>MANDATORY: URL of Ranger Admin. Separate several URLs with commas for Ranger Admin
high availability.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.service.name</name>
<value>dev_nestedstructure</value>
<description>MANDATORY: Name of the Ranger service whose policies are enforced.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.source.impl</name>
<value>org.apache.ranger.admin.client.RangerAdminRESTClient</value>
<description>Class that retrieves policies. The default downloads them from Ranger Admin over
REST.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.rest.ssl.config.file</name>
<value>ranger-nestedstructure-policymgr-ssl.xml</value>
<description>Path of the TLS client configuration file
(ranger-nestedstructure-policymgr-ssl.xml). Needed only when Ranger Admin uses HTTPS. Default:
not set.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.rest.client.connection.timeoutMs</name>
<value>120000</value>
<description>Connect timeout for calls to Ranger Admin. Unit: milliseconds.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.rest.client.read.timeoutMs</name>
<value>30000</value>
<description>Read timeout for calls to Ranger Admin. Unit: milliseconds.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.rest.client.max.retry.attempts</name>
<value>3</value>
<description>Number of retries for a failed call to Ranger Admin.</description>
</property>
<!-- Policy refresh and cache -->
<property>
<name>ranger.plugin.nestedstructure.policy.pollIntervalMs</name>
<value>30000</value>
<description>Interval between policy refreshes. Unit: milliseconds.</description>
</property>
<property>
<name>ranger.plugin.nestedstructure.policy.cache.dir</name>
<value>/etc/ranger/dev_nestedstructure/policycache</value>
<description>Directory for the on-disk policy cache. It must be writable by the process that
hosts the plugin. Default: not set.</description>
</property>
</configuration>
ranger-nestedstructure-audit.xml¶
This file selects where the plugin sends audit events; place it next to
ranger-nestedstructure-security.xml. Each destination is switched on with
xasecure.audit.destination.<name>=true and configured with properties under the same prefix. No property
is mandatory: without an enabled destination, no audit events are stored. The example sends audits to Solr.
<configuration>
<!-- General -->
<property>
<name>xasecure.audit.is.enabled</name>
<value>true</value>
<description>Master switch for auditing in this plugin.</description>
</property>
<property>
<name>xasecure.audit.provider.summary.enabled</name>
<value>false</value>
<description>Collapse events that differ only in time into one event with a count.</description>
</property>
<!-- Audit Server destination -->
<property>
<name>xasecure.audit.destination.auditserver</name>
<value>false</value>
<description>Send audits to the Ranger Audit Server.</description>
</property>
<property>
<name>xasecure.audit.destination.auditserver.url</name>
<value></value>
<description>Audit Server URL. Default: not set.</description>
</property>
<!-- Solr destination -->
<property>
<name>xasecure.audit.destination.solr</name>
<value>true</value>
<description>Send audits to Apache Solr. Default: false.</description>
</property>
<property>
<name>xasecure.audit.destination.solr.urls</name>
<value>http://solr:8983/solr/ranger_audits</value>
<description>Solr collection URLs, separated by commas. Ignored when
xasecure.audit.destination.solr.zookeepers is set. Default: not set.</description>
</property>
<property>
<name>xasecure.audit.destination.solr.zookeepers</name>
<value></value>
<description>ZooKeeper connect string of a SolrCloud cluster. Default: not set.</description>
</property>
<property>
<name>xasecure.audit.destination.solr.batch.filespool.dir</name>
<value>/var/log/myapp/audit/solr/spool</value>
<description>Local directory where events are spooled while Solr is unreachable. Every enabled
destination has the same property under its own prefix. Default: not set.</description>
</property>
<property>
<name>xasecure.audit.destination.solr.collection</name>
<value>ranger_audits</value>
<description>Collection name when ZooKeeper is used.</description>
</property>
<!-- Elasticsearch destination -->
<property>
<name>xasecure.audit.destination.elasticsearch</name>
<value>false</value>
<description>Send audits to Elasticsearch.</description>
</property>
<property>
<name>xasecure.audit.destination.elasticsearch.urls</name>
<value></value>
<description>Elasticsearch host names, separated by commas. Default: not set.</description>
</property>
<property>
<name>xasecure.audit.destination.elasticsearch.port</name>
<value>9200</value>
<description>REST port of the Elasticsearch cluster.</description>
</property>
<property>
<name>xasecure.audit.destination.elasticsearch.protocol</name>
<value>http</value>
<description>One of: http, https.</description>
</property>
<property>
<name>xasecure.audit.destination.elasticsearch.index</name>
<value>ranger_audits</value>
<description>Index that receives the events.</description>
</property>
<!-- HDFS destination -->
<property>
<name>xasecure.audit.destination.hdfs</name>
<value>false</value>
<description>Write audits as files to HDFS or a Hadoop-compatible object store.</description>
</property>
<property>
<name>xasecure.audit.destination.hdfs.dir</name>
<value></value>
<description>Base directory, for example hdfs://namenode:8020/ranger/audit. Default: not
set.</description>
</property>
<!-- Log4j destination -->
<property>
<name>xasecure.audit.destination.log4j</name>
<value>false</value>
<description>Write audits as JSON to a logger of the host process.</description>
</property>
<property>
<name>xasecure.audit.destination.log4j.logger</name>
<value>ranger.audit.log4j</value>
<description>Logger name used by the log4j destination.</description>
</property>
</configuration>
Give every enabled destination a spool directory (xasecure.audit.destination.<name>.batch.filespool.dir)
so that events survive an outage of the audit store. The queue, spool, Kerberos and TLS options of each
destination, and the Audit Server client settings, are in the
Audit framework reference.
ranger-nestedstructure-policymgr-ssl.xml¶
This file is needed only when Ranger Admin is reached over https. The plugin loads it from the path set in
ranger.plugin.nestedstructure.policy.rest.ssl.config.file; a file with this name on the classpath is
picked up automatically. No property is mandatory: without a truststore the plugin relies on the default
truststore of the JVM, and the keystore is needed only for two-way TLS. Passwords are not stored in the
file: they are read from a Hadoop credential store (JCEKS) under fixed aliases.
<configuration>
<!-- Keystore (client certificate, two-way TLS) -->
<property>
<name>xasecure.policymgr.clientssl.keystore</name>
<value></value>
<description>Keystore with the plugin's client certificate. Needed only when Ranger Admin
requires client certificates. Default: not set.</description>
</property>
<property>
<name>xasecure.policymgr.clientssl.keystore.credential.file</name>
<value></value>
<description>Hadoop credential store (JCEKS) that holds the keystore password under the alias
sslKeyStore. Default: not set.</description>
</property>
<property>
<name>xasecure.policymgr.clientssl.keystore.type</name>
<value>jks</value>
<description>Keystore type.</description>
</property>
<!-- Truststore -->
<property>
<name>xasecure.policymgr.clientssl.truststore</name>
<value>/etc/ranger/dev_nestedstructure/truststore.jks</value>
<description>Truststore that contains the Ranger Admin certificate or its CA. When no truststore
is configured, the default truststore of the JVM is used. Default: not set.</description>
</property>
<property>
<name>xasecure.policymgr.clientssl.truststore.credential.file</name>
<value>jceks://file/etc/ranger/dev_nestedstructure/cred.jceks</value>
<description>Hadoop credential store (JCEKS) that holds the truststore password under the alias
sslTrustStore. Default: not set.</description>
</property>
<property>
<name>xasecure.policymgr.clientssl.truststore.type</name>
<value>jks</value>
<description>Truststore type.</description>
</property>
</configuration>
When Ranger Admin validates client certificates, set commonNameForCertificate in the service configuration
to the CN of the plugin's certificate. See Security hardening.
JVM system properties¶
ranger.nestedstructure.recordfilter.js.allowHostAccess (Boolean, default false) controls whether
record-filter scripts running on GraalJS may access Java classes. Leave it off.
Service definition in Ranger Admin¶
Create a service of type nestedstructure whose name equals ranger.plugin.nestedstructure.service.name.
| Field | Required | Description |
|---|---|---|
commonNameForCertificate |
no | Common name of the plugin's client certificate for TLS-authenticated policy download. |
policy.download.auth.users |
no | Users allowed to download policies when Ranger Admin is Kerberized. |
The service definition has no implClass, so there is no Test Connection and no autocomplete; type schema
and field names by hand.
Resources and permissions¶
From ranger-servicedef-nestedstructure.json. Both resources accept wildcards, are matched
case-insensitively, and may end a policy. Deny items and exceptions are enabled.
| Resource | Parent | Description |
|---|---|---|
schema |
— | Logical name of the JSON structure, for example json_object.cxt.cmt.product.vnull3. |
field |
schema |
Dot path of a field, for example store.bicycle.color. |
Access types: read, write.
- Data masking on
schema/fieldforread:MASK,MASK_SHOW_LAST_4,MASK_SHOW_FIRST_4,MASK_HASH,MASK_NULL,MASK_NONE,MASK_DATE_SHOW_YEAR,CUSTOM. Masking is done in Java by the library (DataMasker), not by a query engine. - Row filter on
schemaforreadandwrite: the filter expression is a JavaScript boolean expression evaluated against each record. - No policy conditions or context enrichers are declared.
Behavior notes¶
NestedStructureAuthorizer.authorize() evaluates a record in three steps:
- Schema access — an access request on
schemawith matching scope self or descendants: the user must haveread/writeon the schema or on at least one field in it. Otherwise access is denied. - Record filter — row-filter policies for the schema are evaluated. If a filter applies, its JavaScript
expression is run against the record; a
falseresult denies the record (and is audited as a denial). - Field access and masking — every field in the document is checked individually. If any field is not allowed, the whole record is denied. For allowed fields the masking policies are evaluated and the value is rewritten in the returned JSON.
Field path syntax in policies:
- Maps use dot notation:
store.bicycle.color. - Arrays require an explicit wildcard:
store.book[*]priceorstore.book.*.price. - Only primitive values (strings, numbers, booleans) can be masked; containers cannot be masked as a whole.
If a mask type does not support the data type of the value (not supported in the table below), or a
date cannot be parsed,
DataMaskerthrows aMaskingExceptionandauthorize()returns a result without access and with the error attached.
Mask semantics implemented by DataMasker:
| Mask type | Strings | Numbers | Booleans |
|---|---|---|---|
MASK |
replaced by * (5 to 30 characters) |
-11111 |
false |
MASK_SHOW_LAST_4 / MASK_SHOW_FIRST_4 |
all but the last/first four characters replaced by x |
not supported | not supported |
MASK_HASH |
SHA-256 hex of the value | not supported | not supported |
MASK_DATE_SHOW_YEAR |
parsable ISO/RFC-1123 dates reduced to the year | not supported | not supported |
MASK_NULL |
null |
null |
null |
MASK_NONE |
unchanged | unchanged | unchanged |
CUSTOM |
the literal from the policy | the literal parsed as a long integer | the literal parsed as a boolean |
Auditing¶
NestedStructureAuditHandler buffers the events of one authorize() call and flushes them together:
access decisions per field, a masking event for each field that was masked, and a denial when a record
filter rejects the record. When the call produced at least one denial, only the denied events are written. Service type is nestedstructure; the resource is schema/field.
Further reading¶
plugin-nestedstructure/README.md- Row filter and column masking
- Writing a custom plugin — the same embedding pattern for your own resources
- Authorization API — the newer API for embedding Ranger in an application