This is an automated email from the ASF dual-hosted git repository.
ramackri pushed a commit to branch ranger-2.9
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/ranger-2.9 by this push:
new 3c6cbd3be RANGER-5687: Backport ozone action-matcher feature flag to
ranger-2.9 (#1094)
3c6cbd3be is described below
commit 3c6cbd3bee736dcdcca329aa3585316b541d5e67
Author: Ramachandran Krishnan <[email protected]>
AuthorDate: Wed Jul 22 11:01:02 2026 +0530
RANGER-5687: Backport ozone action-matcher feature flag to ranger-2.9
(#1094)
* RANGER-5687: Backport ozone action-matcher feature flag to ranger-2.9
Back-port of master commit 37b6e02c4d3b989790bbd669b7d5d4366f7f7644 (#1073).
Add runtime feature flag
ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition,
wire FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION in setup/docker, and gate
ozone
action-matches in RangerServiceDefService and RangerOzoneAuthorizer. J10065
is
unchanged (schema sync only); flag behavior is runtime-only per merged
master design.
* Fix PMD unused import in TestRangerServiceDefService.
Remove unused java.util.Map import introduced during RANGER-5687 backport.
* Remove unused java.util.Map import from TestRangerServiceDefService.
Fixes PMD UnusedImports failure in security-admin-web CI.
* RANGER-5687: Apply UI add-on patch for ozone action-matcher backport.
Integrate Dhaval Rajpara add-on patch for PR #1094: fix policy condition
save/load in AddUpdatePolicyForm when action-matches is toggled via the feature
flag, and align XAUtils with ranger-2.9.
---------
Co-authored-by: ramk <[email protected]>
---
dev-support/ranger-docker/README.md | 122 ++++++-
.../admin/ranger-admin-install-mysql.properties | 3 +
.../admin/ranger-admin-install-oracle.properties | 3 +
.../admin/ranger-admin-install-postgres.properties | 43 ++-
.../ranger-admin-install-sqlserver.properties | 3 +
.../ozone/authorizer/RangerOzoneAuthorizer.java | 382 ++++++++++++---------
.../authorizer/TestRangerOzoneAuthorizer.java | 131 +++++++
security-admin/scripts/install.properties | 16 +
security-admin/scripts/setup.sh | 75 ++++
.../ranger/service/RangerServiceDefService.java | 253 ++++++++++----
.../conf.dist/ranger-admin-default-site.xml | 38 +-
.../main/resources/conf.dist/ranger-admin-site.xml | 47 +++
.../main/webapp/react-webapp/src/utils/XAUtils.js | 2 +-
.../views/PolicyListing/AddUpdatePolicyForm.jsx | 12 +-
.../service/TestRangerServiceDefService.java | 114 +++++-
15 files changed, 984 insertions(+), 260 deletions(-)
diff --git a/dev-support/ranger-docker/README.md
b/dev-support/ranger-docker/README.md
index 64276a4ef..4c09881c5 100644
--- a/dev-support/ranger-docker/README.md
+++ b/dev-support/ranger-docker/README.md
@@ -30,11 +30,11 @@ Use Dockerfiles in this directory to create docker images
and run them to build
- Set ```dev-support/ranger-docker``` as your working directory.
-- Execute following command to download necessary archives to setup
Ranger/HDFS/Hive/HBase/Kafka/Knox/Ozone services:
+- Execute following command to download necessary archives to setup
Ranger/HDFS/Hive/HBase/Kafka/Knox/Ozone/OpenSearch services:
~~~
chmod +x download-archives.sh
# use a subset of the below to download specific services
- ./download-archives.sh hadoop hive hbase kafka knox ozone
+ ./download-archives.sh hadoop hive hbase kafka knox ozone opensearch
~~~
- Execute following commands to set environment variables to build Apache
Ranger docker containers:
@@ -50,6 +50,9 @@ Use Dockerfiles in this directory to create docker images and
run them to build
Execute following command to build Apache Ranger:
~~~
+
+chmod +x scripts/**/*.sh
+
# optional step: a fresh build ensures that the correct jdk version is used
docker compose -f docker-compose.ranger-build.yml build
@@ -91,14 +94,112 @@ docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-hadoop.yml
docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-hadoop.yml -f docker-compose.ranger-hbase.yml up -d
~~~
#### Bring up ozone containers
+
+##### Ozone action-matcher feature flag
+
+The flag is controlled at runtime by
+`ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition` in
+`ranger-admin-site.xml`. Docker maps it from
+`FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION` in
+`scripts/admin/ranger-admin-install-*.properties` during Admin setup.
+
+**Prerequisite:** the admin distribution must include an uncommented property
+entry in `conf.dist/ranger-admin-site.xml`. Rebuild dist before building the
+Admin image:
+
+~~~
+docker compose -f docker-compose.ranger-build.yml build
+docker compose -f docker-compose.ranger-build.yml up
+~~~
+
+**First-time setup:** set `FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=true` in
+`scripts/admin/ranger-admin-install-<db>.properties`, then bring up services.
+
+**Changing the flag later** (choose one):
+
+1. Edit `FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION` in
+ `scripts/admin/ranger-admin-install-<db>.properties` and recreate Admin so
+ setup runs again:
+ ~~~
+ docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-ozone.yml \
+ up -d --build --force-recreate ranger
+ ~~~
+ Works even if the DB already exists; setup re-runs on a fresh container.
+
+2. Edit the live value in the container's
+ `ranger-admin-site.xml` and restart Admin.
+
+`install.properties` is not re-read on restart alone — only when setup runs.
+`RangerServiceDefService` applies the site.xml value when serving the ozone
+service-def.
+
~~~
./scripts/ozone/ozone-plugin-docker-setup.sh
docker compose -f docker-compose.ranger.yml -f docker-compose.ranger-ozone.yml
up -d
~~~
+
+Verify (after login):
+
+~~~
+curl -s -u admin:rangerR0cks!
http://localhost:6080/service/plugins/definitions/name/ozone \
+ | python3 -c "import json,sys; d=json.load(sys.stdin); print('options',
d.get('options')); print('conditions', [c['name'] for c in
d.get('policyConditions',[])])"
+~~~
#### Bring up trino container (requires docker build with jdk 11):
~~~
docker compose -f docker-compose.ranger.yml -f docker-compose.ranger-trino.yml
up -d
~~~
+#### Bring up opensearch container:
+~~~
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-opensearch.yml up -d
+~~~
+
+#### OpenSearch audit flow (replace Solr for access audits)
+
+OpenSearch can replace Solr for **audit storage and UI queries**. Ranger Admin
reads audits via
+`audit_store=opensearch` using a native low-level REST client (compatible with
any OpenSearch version).
+
+**Write path:** access audits flow through audit-server ingestor, Kafka, and
the Java
+`ranger-audit-dispatcher-opensearch` service into the OpenSearch
`ranger_audits` index.
+Ranger Admin policy/admin transaction audits remain DB-backed; this is the
same boundary
+used by the Solr audit path.
+
+##### Setup
+
+~~~
+# Prerequisites: build the audit-dispatcher tarball and download archives
+mvn clean package -DskipTests -pl distro -am
+cp target/ranger-*-audit-dispatcher.tar.gz dev-support/ranger-docker/dist/
+cd dev-support/ranger-docker
+./download-archives.sh kafka opensearch hadoop
+
+export RANGER_DB_TYPE=postgres
+
+# 1. Start OpenSearch first (Ranger Admin's bootstrapper needs it on startup)
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-opensearch.yml \
+ -f docker-compose.ranger-kafka.yml -f docker-compose.ranger-hadoop.yml \
+ -f docker-compose.ranger-audit-server.yml \
+ -f docker-compose.ranger-audit-dispatcher-opensearch.yml up -d
ranger-opensearch
+
+# 2. Start core stack (Ranger Admin, Kafka, Hadoop)
+# Kafka auto-creates the ranger_audits topic on startup.
+# Ranger Admin auto-creates the OpenSearch index via
OpenSearchIndexBootStrapper.
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-opensearch.yml \
+ -f docker-compose.ranger-kafka.yml -f docker-compose.ranger-hadoop.yml \
+ -f docker-compose.ranger-audit-server.yml \
+ -f docker-compose.ranger-audit-dispatcher-opensearch.yml up -d ranger
ranger-kafka ranger-hadoop
+
+# 3. Start audit ingestor and OpenSearch dispatcher
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-opensearch.yml \
+ -f docker-compose.ranger-kafka.yml -f docker-compose.ranger-hadoop.yml \
+ -f docker-compose.ranger-audit-server.yml \
+ -f docker-compose.ranger-audit-dispatcher-opensearch.yml up -d
ranger-audit-ingestor ranger-audit-dispatcher-opensearch
+~~~
+
+For **fresh Ranger installs** using OpenSearch for audits, set
`audit_store=opensearch` in
+`scripts/admin/ranger-admin-install-postgres.properties` and configure the
`audit_opensearch_*` properties.
+
+For **existing Solr-based installs**, switching stores requires updating
`audit_store=opensearch`
+in install.properties, configuring the `audit_opensearch_*` properties, and
restarting Ranger Admin.
Similarly, check the `depends` section of the
`docker-compose.ranger-service.yaml` file and add docker-compose files for
these services when trying to bring up the `service` container.
#### Bring up all containers
@@ -111,3 +212,20 @@ docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-usersync.ym
~~~
docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-usersync.yml -f docker-compose.ranger-tagsync.yml -f
docker-compose.ranger-kms.yml -f docker-compose.ranger-hadoop.yml -f
docker-compose.ranger-hbase.yml -f docker-compose.ranger-kafka.yml -f
docker-compose.ranger-hive.yml -f docker-compose.ranger-trino.yml -f
docker-compose.ranger-knox.yml up -d --no-deps --force-recreate --build
<service-1> <service-2>
~~~
+
+#### To bring up audit server ingestor + dispatchers. Make sure kafka, solr,
and hdfs containers are running before bringing up audit server services.
+~~~
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-hadoop.yml -f docker-compose.ranger-kafka.yml -f
docker-compose.ranger-audit-server.yml up -d
+~~~
+
+#### To bring up audit server services individually:
+~~~
+# Audit ingestor
+docker compose -f docker-compose.ranger.yml -f docker-compose.ranger-kafka.yml
-f docker-compose.ranger-audit-ingestor.yml up -d
+
+# Solr dispatcher
+docker compose -f docker-compose.ranger.yml -f docker-compose.ranger-kafka.yml
-f docker-compose.ranger-audit-dispatcher-solr.yml up -d
+
+# HDFS dispatcher
+docker compose -f docker-compose.ranger.yml -f
docker-compose.ranger-hadoop.yml -f docker-compose.ranger-kafka.yml -f
docker-compose.ranger-audit-dispatcher-hdfs.yml up -d
+~~~
diff --git
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties
index 18a4c8da7..14b4330ad 100644
---
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties
+++
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-mysql.properties
@@ -59,6 +59,9 @@ audit_elasticsearch_bootstrap_enabled=true
policymgr_external_url=http://ranger-admin:6080
policymgr_http_enabled=true
+# Enable Ozone action-matches policy condition (maps to ranger-admin-site.xml
at setup)
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=false
+
unix_user=ranger
unix_user_pwd=ranger
unix_group=ranger
diff --git
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties
index 22b177c02..4748dc0dc 100644
---
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties
+++
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-oracle.properties
@@ -61,6 +61,9 @@ audit_elasticsearch_bootstrap_enabled=true
policymgr_external_url=http://ranger-admin:6080
policymgr_http_enabled=true
+# Enable Ozone action-matches policy condition (maps to ranger-admin-site.xml
at setup)
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=false
+
unix_user=ranger
unix_user_pwd=ranger
unix_group=ranger
diff --git
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties
index cf8a58fec..80c18df4e 100644
---
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties
+++
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-postgres.properties
@@ -43,22 +43,40 @@ rangerUsersync_password=rangerR0cks!
keyadmin_password=rangerR0cks!
-audit_store=solr
-audit_solr_urls=http://ranger-solr:8983/solr/ranger_audits
-audit_solr_collection_name=ranger_audits
+## --- Audit store: enable ONE of the following blocks ---
+## Option 1: Solr (default)
+# audit_store=solr
+# FQDN required for SPNEGO to Solr (HTTP/ranger-solr.rangernw@REALM)
+# audit_solr_urls=http://ranger-solr.rangernw:8983/solr/ranger_audits
+# audit_solr_collection_name=ranger_audits
+
+## Option 2: OpenSearch / Elasticsearch (legacy - uses ES HighLevel REST
Client)
# audit_store=elasticsearch
-audit_elasticsearch_urls=
-audit_elasticsearch_port=9200
-audit_elasticsearch_protocol=http
-audit_elasticsearch_user=elastic
-audit_elasticsearch_password=elasticsearch
-audit_elasticsearch_index=ranger_audits
-audit_elasticsearch_bootstrap_enabled=true
+# audit_elasticsearch_urls=ranger-opensearch
+# audit_elasticsearch_port=9200
+# audit_elasticsearch_protocol=http
+# audit_elasticsearch_user=NONE
+# audit_elasticsearch_password=NONE
+# audit_elasticsearch_index=ranger_audits
+# audit_elasticsearch_bootstrap_enabled=true
+
+## Option 3: OpenSearch (native - uses low-level REST client, works with any
OpenSearch version)
+audit_store=opensearch
+audit_opensearch_urls=ranger-opensearch
+audit_opensearch_port=9200
+audit_opensearch_protocol=http
+audit_opensearch_user=admin
+audit_opensearch_password=admin
+audit_opensearch_index=ranger_audits
+audit_opensearch_bootstrap_enabled=true
policymgr_external_url=http://ranger-admin:6080
policymgr_http_enabled=true
+# Enable Ozone action-matches policy condition (maps to ranger-admin-site.xml
at setup)
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=false
+
unix_user=ranger
unix_user_pwd=ranger
unix_group=ranger
@@ -103,7 +121,8 @@
audit_jaas_client_loginModuleName=com.sun.security.auth.module.Krb5LoginModule
audit_jaas_client_loginModuleControlFlag=required
audit_jaas_client_option_useKeyTab=true
audit_jaas_client_option_storeKey=true
-audit_jaas_client_option_useTicketCache=true
-audit_jaas_client_option_serviceName=ranger
+# Leave empty: SolrJ derives HTTP service from audit URL host (must be FQDN)
+audit_jaas_client_option_serviceName=
+audit_jaas_client_option_useTicketCache=false
audit_jaas_client_option_keyTab=/etc/keytabs/rangeradmin.keytab
audit_jaas_client_option_principal=rangeradmin/[email protected]
diff --git
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties
index 8a1919374..40f77c3fd 100644
---
a/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties
+++
b/dev-support/ranger-docker/scripts/admin/ranger-admin-install-sqlserver.properties
@@ -68,6 +68,9 @@ audit_elasticsearch_bootstrap_enabled=true
policymgr_external_url=http://ranger-admin:6080
policymgr_http_enabled=true
+# Enable Ozone action-matches policy condition (maps to ranger-admin-site.xml
at setup)
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=false
+
unix_user=ranger
unix_user_pwd=ranger
unix_group=ranger
diff --git
a/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
b/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
index 44159ce92..95094bd10 100644
---
a/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
+++
b/plugin-ozone/src/main/java/org/apache/ranger/authorization/ozone/authorizer/RangerOzoneAuthorizer.java
@@ -41,6 +41,7 @@
import org.apache.ranger.plugin.service.RangerBasePlugin;
import org.apache.ranger.plugin.util.JsonUtilsV2;
import org.apache.ranger.plugin.util.RangerPerfTracer;
+import org.apache.ranger.plugin.util.ServiceDefUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,159 +55,171 @@
import java.util.stream.Collectors;
public class RangerOzoneAuthorizer implements IAccessAuthorizer {
- public static final String ACCESS_TYPE_READ = "read";
- public static final String ACCESS_TYPE_WRITE = "write";
- public static final String ACCESS_TYPE_CREATE = "create";
- public static final String ACCESS_TYPE_LIST = "list";
- public static final String ACCESS_TYPE_DELETE = "delete";
- public static final String ACCESS_TYPE_READ_ACL = "read_acl";
- public static final String ACCESS_TYPE_WRITE_ACL = "write_acl";
- public static final String ACCESS_TYPE_ASSUME_ROLE = "assume_role";
- public static final String ACCESS_TYPE_ALL = "all";
+ private static final Logger LOG =
LoggerFactory.getLogger(RangerOzoneAuthorizer.class);
+ private static final Logger PERF_OZONEAUTH_REQUEST_LOG =
RangerPerfTracer.getPerfLogger("ozoneauth.request");
- public static final String KEY_RESOURCE_VOLUME = "volume";
- public static final String KEY_RESOURCE_BUCKET = "bucket";
- public static final String KEY_RESOURCE_KEY = "key";
- public static final String KEY_RESOURCE_ROLE = "role";
+ public static final String ACCESS_TYPE_READ = "read";
+ public static final String ACCESS_TYPE_WRITE = "write";
+ public static final String ACCESS_TYPE_CREATE = "create";
+ public static final String ACCESS_TYPE_LIST = "list";
+ public static final String ACCESS_TYPE_DELETE = "delete";
+ public static final String ACCESS_TYPE_READ_ACL = "read_acl";
+ public static final String ACCESS_TYPE_WRITE_ACL = "write_acl";
+ public static final String ACCESS_TYPE_ASSUME_ROLE = "assume_role";
+ public static final String ACCESS_TYPE_ALL = "all";
- private static final String S3_VOLUME_NAME = "s3Vol";
+ public static final String KEY_RESOURCE_VOLUME = "volume";
+ public static final String KEY_RESOURCE_BUCKET = "bucket";
+ public static final String KEY_RESOURCE_KEY = "key";
+ public static final String KEY_RESOURCE_ROLE = "role";
- private static final Logger PERF_OZONEAUTH_REQUEST_LOG =
RangerPerfTracer.getPerfLogger("ozoneauth.request");
+ private static final String S3_VOLUME_NAME = "s3Vol";
- private static final Logger LOG =
LoggerFactory.getLogger(RangerOzoneAuthorizer.class);
+ private static final String
OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION =
"enableActionMatcherInPoliciesCondition";
- private static volatile RangerBasePlugin rangerPlugin = null;
+ private static volatile RangerBasePlugin rangerPlugin;
- public RangerOzoneAuthorizer() {
- RangerBasePlugin plugin = rangerPlugin;
+ public RangerOzoneAuthorizer() {
+ RangerBasePlugin plugin = rangerPlugin;
+
+ if (plugin == null) {
+ synchronized (RangerOzoneAuthorizer.class) {
+ plugin = rangerPlugin;
- if (plugin == null) {
- synchronized (RangerOzoneAuthorizer.class) {
- plugin = rangerPlugin;
+ if (plugin == null) {
+ plugin = new RangerBasePlugin("ozone", "ozone");
- if (plugin == null) {
- plugin = new RangerBasePlugin("ozone",
"ozone");
- plugin.init(); // this will initialize
policy engine and policy refresher
+ plugin.init(); // this will initialize policy engine and
policy refresher
- RangerDefaultAuditHandler auditHandler
= new RangerDefaultAuditHandler();
- plugin.setResultProcessor(auditHandler);
+ RangerDefaultAuditHandler auditHandler = new
RangerDefaultAuditHandler();
- rangerPlugin = plugin;
- }
- }
- }
- }
+ plugin.setResultProcessor(auditHandler);
+ rangerPlugin = plugin;
+ }
+ }
+ }
+ }
+
+ // for testing only
RangerOzoneAuthorizer(RangerBasePlugin plugin) {
rangerPlugin = plugin;
}
- @Override
- public boolean checkAccess(IOzoneObj ozoneObject, RequestContext
context) {
- boolean returnValue = false;
- if (ozoneObject == null) {
- LOG.error("Ozone object is null!!");
- return returnValue;
- }
- OzoneObj ozoneObj = (OzoneObj) ozoneObject;
- UserGroupInformation ugi = context.getClientUgi();
- ACLType operation = context.getAclRights();
- String resource = ozoneObj.getPath();
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("==> RangerOzoneAuthorizer.checkAccess with
operation = " + operation + ", resource = " +
- resource + ", store type = " +
OzoneObj.StoreType.values() + ", ugi = " + ugi + ", ip = " +
- context.getIp() + ", resourceType = " +
ozoneObj.getResourceType() + ")");
- }
-
- RangerBasePlugin plugin = rangerPlugin;
-
- if (plugin == null) {
- MiscUtil.logErrorMessageByInterval(LOG,
- "Authorizer is still not initialized");
- return returnValue;
- }
-
- //TODO: If sorce type is S3 and resource is volume, then allow
it by default
- if (ozoneObj.getStoreType() == OzoneObj.StoreType.S3 &&
ozoneObj.getResourceType() == OzoneObj.ResourceType.VOLUME) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("If store type is s3 and resource is
volume, then we allow it by default! Returning true");
- }
- LOG.warn("Allowing access by default since source type
is S3 and resource type is Volume!!");
- return true;
- }
-
- RangerPerfTracer perf = null;
-
- if
(RangerPerfTracer.isPerfTraceEnabled(PERF_OZONEAUTH_REQUEST_LOG)) {
- perf =
RangerPerfTracer.getPerfTracer(PERF_OZONEAUTH_REQUEST_LOG,
"RangerOzoneAuthorizer.authorize(resource=" + resource + ")");
- }
-
- Date eventTime = new Date();
- String accessType = mapToRangerAccessType(operation);
- if (accessType == null) {
- MiscUtil.logErrorMessageByInterval(LOG,
- "Unsupported access type. operation=" +
operation) ;
- LOG.error("Unsupported access type. operation=" +
operation + ", resource=" + resource);
- return returnValue;
- }
- String clusterName = plugin.getClusterName();
-
- RangerAccessRequestImpl rangerRequest = new
RangerAccessRequestImpl();
- rangerRequest.setUser(ugi.getShortUserName());
-
rangerRequest.setUserGroups(Sets.newHashSet(ugi.getGroupNames()));
-
rangerRequest.setClientIPAddress(context.getIp().getHostAddress());
-
rangerRequest.setRemoteIPAddress(context.getIp().getHostAddress());
- rangerRequest.setAccessTime(eventTime);
-
- RangerAccessResourceImpl rangerResource = new
RangerAccessResourceImpl();
- rangerResource.setOwnerUser(context.getOwnerName());
-
- rangerRequest.setResource(rangerResource);
- rangerRequest.setAccessType(accessType);
- rangerRequest.setAction(context.getS3Action());
- rangerRequest.setRequestData(resource);
- rangerRequest.setClusterName(clusterName);
-
- if (ozoneObj.getResourceType() == OzoneObj.ResourceType.VOLUME)
{
- rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getVolumeName());
- } else if (ozoneObj.getResourceType() ==
OzoneObj.ResourceType.BUCKET || ozoneObj.getResourceType() ==
OzoneObj.ResourceType.KEY) {
- rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getStoreType() == OzoneObj.StoreType.S3 ? S3_VOLUME_NAME :
ozoneObj.getVolumeName());
- rangerResource.setValue(KEY_RESOURCE_BUCKET,
ozoneObj.getBucketName());
- if (ozoneObj.getResourceType() ==
OzoneObj.ResourceType.KEY) {
- rangerResource.setValue(KEY_RESOURCE_KEY,
ozoneObj.getKeyName());
- }
- } else {
- LOG.error("Unsupported resource = " + resource);
- MiscUtil.logErrorMessageByInterval(LOG, "Unsupported
resource type " + ozoneObj.getResourceType() + " for resource = " + resource
- + ", request=" + rangerRequest);
- return returnValue;
- }
-
- try {
- if (StringUtils.isNotBlank(context.getSessionPolicy()))
{
-
rangerRequest.setInlinePolicy(JsonUtilsV2.jsonToObj(context.getSessionPolicy(),
RangerInlinePolicy.class));
- }
-
- RangerAccessResult result = plugin
- .isAccessAllowed(rangerRequest);
- if (result == null) {
- LOG.error("Ranger Plugin returned null.
Returning false");
- } else {
- returnValue = result.getIsAllowed();
- }
- } catch (Throwable t) {
- LOG.error("Error while calling isAccessAllowed().
request="
- + rangerRequest, t);
- }
- RangerPerfTracer.log(perf);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("rangerRequest=" + rangerRequest + ", return="
- + returnValue);
- }
- return returnValue;
- }
+ @Override
+ public boolean checkAccess(IOzoneObj ozoneObject, RequestContext context) {
+ boolean returnValue = false;
+
+ if (ozoneObject == null) {
+ LOG.error("Ozone object is null!!");
+
+ return false;
+ }
+
+ OzoneObj ozoneObj = (OzoneObj) ozoneObject;
+ UserGroupInformation ugi = context.getClientUgi();
+ ACLType operation = context.getAclRights();
+ String resource = ozoneObj.getPath();
+
+ LOG.debug("==> RangerOzoneAuthorizer.checkAccess with operation = {},
resource = {}, store type = {}, ugi = {}, ip = {}, resourceType = {}",
operation, resource, OzoneObj.StoreType.values(), ugi, context.getIp(),
ozoneObj.getResourceType());
+
+ RangerBasePlugin plugin = rangerPlugin;
+
+ if (plugin == null) {
+ MiscUtil.logErrorMessageByInterval(LOG, "Authorizer is still not
initialized");
+
+ return false;
+ }
+
+ //TODO: If source type is S3 and resource is volume, then allow it by
default
+ if (ozoneObj.getStoreType() == OzoneObj.StoreType.S3 &&
ozoneObj.getResourceType() == OzoneObj.ResourceType.VOLUME) {
+ LOG.debug("If store type is s3 and resource is volume, then we
allow it by default! Returning true");
+ LOG.warn("Allowing access by default since source type is S3 and
resource type is Volume!!");
+
+ return true;
+ }
+
+ RangerPerfTracer perf = null;
+
+ if (RangerPerfTracer.isPerfTraceEnabled(PERF_OZONEAUTH_REQUEST_LOG)) {
+ perf = RangerPerfTracer.getPerfTracer(PERF_OZONEAUTH_REQUEST_LOG,
String.format("RangerOzoneAuthorizer.authorize(resource = %s)", resource));
+ }
+
+ Date eventTime = new Date();
+ String accessType = mapToRangerAccessType(operation);
+
+ if (accessType == null) {
+ String message = String.format("Unsupported access type. operation
= %s", operation);
+
+ MiscUtil.logErrorMessageByInterval(LOG, message);
+ LOG.error("{}, resource = {}", message, resource);
+
+ return false;
+ }
+
+ String clusterName = plugin.getClusterName();
+ RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl();
+
+ rangerRequest.setUser(ugi.getShortUserName());
+ rangerRequest.setUserGroups(Sets.newHashSet(ugi.getGroupNames()));
+ rangerRequest.setClientIPAddress(context.getIp().getHostAddress());
+ rangerRequest.setRemoteIPAddress(context.getIp().getHostAddress());
+ rangerRequest.setAccessTime(eventTime);
+
+ RangerAccessResourceImpl rangerResource = new
RangerAccessResourceImpl();
+
+ rangerResource.setOwnerUser(context.getOwnerName());
+ rangerRequest.setResource(rangerResource);
+ rangerRequest.setAccessType(accessType);
+ if (isOzoneActionPolicyEnabled(plugin)) {
+ rangerRequest.setAction(context.getS3Action());
+ } else {
+ rangerRequest.setAction(accessType);
+ }
+ rangerRequest.setRequestData(resource);
+ rangerRequest.setClusterName(clusterName);
+
+ if (ozoneObj.getResourceType() == OzoneObj.ResourceType.VOLUME) {
+ rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getVolumeName());
+ } else if (ozoneObj.getResourceType() == OzoneObj.ResourceType.BUCKET
|| ozoneObj.getResourceType() == OzoneObj.ResourceType.KEY) {
+ rangerResource.setValue(KEY_RESOURCE_VOLUME,
ozoneObj.getStoreType() == OzoneObj.StoreType.S3 ? S3_VOLUME_NAME :
ozoneObj.getVolumeName());
+ rangerResource.setValue(KEY_RESOURCE_BUCKET,
ozoneObj.getBucketName());
+
+ if (ozoneObj.getResourceType() == OzoneObj.ResourceType.KEY) {
+ rangerResource.setValue(KEY_RESOURCE_KEY,
ozoneObj.getKeyName());
+ }
+ } else {
+ LOG.error("Unsupported resource = {}", resource);
+
+ String message = String.format("Unsupported resource type = %s for
resource = %s, request = %s", ozoneObj.getResourceType(), resource,
rangerRequest);
+ MiscUtil.logErrorMessageByInterval(LOG, message);
+
+ return false;
+ }
+
+ try {
+ if (StringUtils.isNotBlank(context.getSessionPolicy())) {
+ RangerInlinePolicy inlinePolicy =
JsonUtilsV2.jsonToObj(context.getSessionPolicy(), RangerInlinePolicy.class);
+
rangerRequest.setInlinePolicy(sanitizeInlinePolicyForActionFlag(inlinePolicy,
plugin));
+ }
+
+ RangerAccessResult result = plugin.isAccessAllowed(rangerRequest);
+
+ if (result == null) {
+ LOG.error("Ranger Plugin returned null. Returning false");
+ } else {
+ returnValue = result.getIsAllowed();
+ }
+ } catch (Throwable t) {
+ LOG.error("Error while calling isAccessAllowed(). request = {}",
rangerRequest, t);
+ }
+
+ RangerPerfTracer.log(perf);
+
+ LOG.debug("rangerRequest = {}, return = {}", rangerRequest,
returnValue);
+
+ return returnValue;
+ }
@Override
public String generateAssumeRoleSessionPolicy(AssumeRoleRequest
assumeRoleRequest) throws OMException {
@@ -263,33 +276,39 @@ public String
generateAssumeRoleSessionPolicy(AssumeRoleRequest assumeRoleReques
}
}
- private String mapToRangerAccessType(ACLType operation) {
- String rangerAccessType = null;
- switch (operation) {
- case READ:
- rangerAccessType = ACCESS_TYPE_READ;
- break;
- case WRITE:
- rangerAccessType = ACCESS_TYPE_WRITE;
- break;
- case CREATE:
- rangerAccessType = ACCESS_TYPE_CREATE;
- break;
- case DELETE:
- rangerAccessType = ACCESS_TYPE_DELETE;
- break;
- case LIST:
- rangerAccessType = ACCESS_TYPE_LIST;
- break;
- case READ_ACL:
- rangerAccessType = ACCESS_TYPE_READ_ACL;
- break;
- case WRITE_ACL:
- rangerAccessType = ACCESS_TYPE_WRITE_ACL;
- break;
- }
- return rangerAccessType;
- }
+ private String mapToRangerAccessType(ACLType operation) {
+ final String rangerAccessType;
+
+ switch (operation) {
+ case READ:
+ rangerAccessType = ACCESS_TYPE_READ;
+ break;
+ case WRITE:
+ rangerAccessType = ACCESS_TYPE_WRITE;
+ break;
+ case CREATE:
+ rangerAccessType = ACCESS_TYPE_CREATE;
+ break;
+ case DELETE:
+ rangerAccessType = ACCESS_TYPE_DELETE;
+ break;
+ case LIST:
+ rangerAccessType = ACCESS_TYPE_LIST;
+ break;
+ case READ_ACL:
+ rangerAccessType = ACCESS_TYPE_READ_ACL;
+ break;
+ case WRITE_ACL:
+ rangerAccessType = ACCESS_TYPE_WRITE_ACL;
+ break;
+ default:
+ LOG.error("Unknown operation!");
+ rangerAccessType = null;
+ break;
+ }
+
+ return rangerAccessType;
+ }
private static RangerInlinePolicy.Grant toRangerGrant(OzoneGrant
ozoneGrant, RangerBasePlugin plugin) {
RangerInlinePolicy.Grant ret = new RangerInlinePolicy.Grant();
@@ -302,9 +321,12 @@ private static RangerInlinePolicy.Grant
toRangerGrant(OzoneGrant ozoneGrant, Ran
ret.setPermissions(ozoneGrant.getPermissions().stream().map(RangerOzoneAuthorizer::toRangerPermission).filter(Objects::nonNull).collect(Collectors.toSet()));
}
- final Set<String> s3Actions = ozoneGrant.getS3Actions();
- if (s3Actions != null && !s3Actions.isEmpty()) {
- ret.setActions(s3Actions);
+ if (isOzoneActionPolicyEnabled(plugin)) {
+ final Set<String> s3Actions = ozoneGrant.getS3Actions();
+
+ if (s3Actions != null && !s3Actions.isEmpty()) {
+ ret.setActions(s3Actions);
+ }
}
LOG.debug("toRangerGrant(ozoneGrant={}): ret={}", ozoneGrant, ret);
@@ -348,7 +370,6 @@ private static String toRrn(IOzoneObj obj, RangerBasePlugin
plugin) {
return ret;
}
-
private static String toRangerPermission(ACLType acl) {
switch (acl) {
case READ:
@@ -374,5 +395,26 @@ private static String toRangerPermission(ACLType acl) {
return null;
}
-}
+ private static boolean isOzoneActionPolicyEnabled(final RangerBasePlugin
plugin) {
+ return plugin != null
+ && plugin.getServiceDef() != null
+ &&
ServiceDefUtil.getBooleanValue(plugin.getServiceDef().getOptions(),
OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION, false);
+ }
+
+ /**
+ * When action-policy is disabled, strip grant actions so generic inline
evaluation
+ * does not enforce S3 actions (same effect as omitting actions in {@link
#toRangerGrant}).
+ */
+ static RangerInlinePolicy
sanitizeInlinePolicyForActionFlag(RangerInlinePolicy policy, RangerBasePlugin
plugin) {
+ if (policy != null && policy.getGrants() != null &&
!isOzoneActionPolicyEnabled(plugin)) {
+ for (RangerInlinePolicy.Grant grant : policy.getGrants()) {
+ if (grant != null) {
+ grant.setActions(null);
+ }
+ }
+ }
+
+ return policy;
+ }
+}
diff --git
a/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
b/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
index a715e9c6f..7a90e758d 100644
---
a/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
+++
b/plugin-ozone/src/test/java/org/apache/ranger/authorization/ozone/authorizer/TestRangerOzoneAuthorizer.java
@@ -37,7 +37,9 @@
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -53,8 +55,10 @@ public class TestRangerOzoneAuthorizer {
private static final String RANGER_APP_ID = "om";
private static final String OZONE_SERVICE_ID = "om";
private static final String OWNER_NAME = "ozone";
+ private static final String
OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION =
"enableActionMatcherInPoliciesCondition";
private static RangerOzoneAuthorizer ozoneAuthorizer;
+ private static RangerBasePlugin testPlugin;
private final String hostname = "localhost";
private final InetAddress ipAddress =
InetAddress.getLoopbackAddress();
@@ -85,7 +89,9 @@ public static void setUpBeforeClass() {
// loads policies from om_dev_ozone.json, by
EmbeddedResourcePolicySource configured in ranger-ozone-security.xml
plugin.init();
+ setOzoneActionPolicyEnabled(plugin);
+ testPlugin = plugin;
ozoneAuthorizer = new RangerOzoneAuthorizer(plugin);
assertNotNull(ozoneAuthorizer);
@@ -273,4 +279,129 @@ public void testCheckAccessWithS3Action() throws
Exception {
assertFalse(ozoneAuthorizer.checkAccess(key1, ctxWriteNoAction),
"session-policy should deny write on key1 when no S3 action is
specified and grant has S3 actions");
}
+
+ @Test
+ public void testAssumeRoleWithS3ActionsNotPropagatedWhenFlagDisabled()
throws Exception {
+ final String previous = setOzoneActionPolicyDisabled();
+
+ try {
+ final Set<OzoneGrant> grants =
Collections.singleton(grantWriteWithS3Actions);
+ final AssumeRoleRequest request = new AssumeRoleRequest(hostname,
ipAddress, user1, role1, grants);
+
+ final String sessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ assertNotNull(sessionPolicy);
+
+ final RangerInlinePolicy inlinePolicy =
JsonUtilsV2.jsonToObj(sessionPolicy, RangerInlinePolicy.class);
+ final RangerInlinePolicy.Grant grant =
inlinePolicy.getGrants().iterator().next();
+
+ assertNull(grant.getActions(), "S3 actions should not be
propagated when ozone action policy is disabled");
+ } finally {
+ restoreOzoneActionPolicyEnabled(previous);
+ }
+ }
+
+ @Test
+ public void testCheckAccessIgnoresS3ActionWhenFlagDisabled() throws
Exception {
+ final String previous = setOzoneActionPolicyDisabled();
+
+ try {
+ final Set<OzoneGrant> grants =
Collections.singleton(grantWriteWithS3Actions);
+ final AssumeRoleRequest request = new AssumeRoleRequest(hostname,
ipAddress, user1, role1, grants);
+
+ final String sessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ assertNotNull(sessionPolicy);
+
+ final RequestContext ctxWriteGetObject = reqCtxBuilder
+ .setAclRights(IAccessAuthorizer.ACLType.WRITE)
+ .setRecursiveAccessCheck(false)
+ .setSessionPolicy(sessionPolicy)
+ .setS3Action("GetObject")
+ .build();
+
+ assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteGetObject),
+ "session-policy should allow write when S3 action
enforcement is disabled");
+
+ final RequestContext ctxWriteNoAction = reqCtxBuilder
+ .setAclRights(IAccessAuthorizer.ACLType.WRITE)
+ .setRecursiveAccessCheck(false)
+ .setSessionPolicy(sessionPolicy)
+ .setS3Action(null)
+ .build();
+
+ assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteNoAction),
+ "session-policy should allow write when S3 action
enforcement is disabled");
+ } finally {
+ restoreOzoneActionPolicyEnabled(previous);
+ }
+ }
+
+ @Test
+ public void testLegacySessionPolicyActionsStrippedWhenFlagDisabled()
throws Exception {
+ // Simulate a session policy issued when action-policy was enabled
(actions embedded in JSON).
+ Set<OzoneGrant> grants =
Collections.singleton(grantWriteWithS3Actions);
+ AssumeRoleRequest request = new AssumeRoleRequest(hostname, ipAddress,
user1, role1, grants);
+
+ String legacySessionPolicy =
ozoneAuthorizer.generateAssumeRoleSessionPolicy(request);
+
+ RangerInlinePolicy legacyPolicy =
JsonUtilsV2.jsonToObj(legacySessionPolicy, RangerInlinePolicy.class);
+ RangerInlinePolicy.Grant legacyGrant =
legacyPolicy.getGrants().iterator().next();
+
+ assertNotNull(legacyGrant.getActions(),
+ "legacy session policy should contain S3 actions as if created
with flag enabled");
+
+ final String previous = setOzoneActionPolicyDisabled();
+
+ try {
+ RangerInlinePolicy sanitized =
RangerOzoneAuthorizer.sanitizeInlinePolicyForActionFlag(legacyPolicy,
testPlugin);
+
+ assertNull(sanitized.getGrants().iterator().next().getActions(),
+ "sanitizeInlinePolicyForActionFlag should strip grant
actions when flag is disabled");
+
+ RequestContext ctxWriteGetObject = reqCtxBuilder
+ .setAclRights(IAccessAuthorizer.ACLType.WRITE)
+ .setRecursiveAccessCheck(false)
+ .setSessionPolicy(legacySessionPolicy)
+ .setS3Action("GetObject")
+ .build();
+
+ assertTrue(ozoneAuthorizer.checkAccess(key1, ctxWriteGetObject),
+ "legacy session-policy with embedded S3 actions should
allow write when flag is disabled");
+ } finally {
+ restoreOzoneActionPolicyEnabled(previous);
+ }
+ }
+
+ private static void setOzoneActionPolicyEnabled(RangerBasePlugin plugin) {
+ if (plugin == null || plugin.getServiceDef() == null) {
+ return;
+ }
+
+ Map<String, String> options = plugin.getServiceDef().getOptions();
+
+ if (options == null) {
+ options = new HashMap<>();
+ plugin.getServiceDef().setOptions(options);
+ }
+
+ options.put(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
Boolean.toString(true));
+ }
+
+ private static String setOzoneActionPolicyDisabled() {
+ final Map<String, String> options =
testPlugin.getServiceDef().getOptions();
+ final String previous =
options.put(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
Boolean.toString(false));
+
+ return previous;
+ }
+
+ private static void restoreOzoneActionPolicyEnabled(String previous) {
+ final Map<String, String> options =
testPlugin.getServiceDef().getOptions();
+
+ if (previous == null) {
+ options.remove(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION);
+ } else {
+ options.put(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
previous);
+ }
+ }
}
diff --git a/security-admin/scripts/install.properties
b/security-admin/scripts/install.properties
index 0f5a093f6..6a6ababc0 100644
--- a/security-admin/scripts/install.properties
+++ b/security-admin/scripts/install.properties
@@ -101,6 +101,19 @@ audit_elasticsearch_password=
audit_elasticsearch_index=
audit_elasticsearch_bootstrap_enabled=true
+# * OpenSearch audit store properties (when audit_store=opensearch)
+audit_opensearch_urls=
+audit_opensearch_port=9200
+audit_opensearch_protocol=http
+audit_opensearch_user=
+audit_opensearch_password=
+audit_opensearch_index=ranger_audits
+audit_opensearch_bootstrap_enabled=true
+# * OpenSearch authentication scheme: kerberos, basic, or none. Leave empty to
infer from the properties above.
+audit_opensearch_authentication_type=
+audit_opensearch_kerberos_principal=
+audit_opensearch_kerberos_keytab=
+
# * audit_solr_url URL to Solr. E.g. http://<solr_host>:6083/solr/ranger_audits
audit_solr_urls=
@@ -139,6 +152,9 @@ policymgr_https_keystore_password=
#Example : policymgr_supportedcomponents=hive,hbase,hdfs
policymgr_supportedcomponents=
+# Enable Ozone action-matches policy condition in Admin UI and authorizer
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=false
+
#
# ------- PolicyManager CONFIG - END ---------------
#
diff --git a/security-admin/scripts/setup.sh b/security-admin/scripts/setup.sh
index 43ef4b704..b0bc26214 100755
--- a/security-admin/scripts/setup.sh
+++ b/security-admin/scripts/setup.sh
@@ -102,6 +102,16 @@ audit_elasticsearch_user=$(get_prop
'audit_elasticsearch_user' $PROPFILE)
audit_elasticsearch_password=$(get_prop 'audit_elasticsearch_password'
$PROPFILE)
audit_elasticsearch_index=$(get_prop 'audit_elasticsearch_index' $PROPFILE)
audit_elasticsearch_bootstrap_enabled=$(get_prop
'audit_elasticsearch_bootstrap_enabled' $PROPFILE)
+audit_opensearch_urls=$(get_prop 'audit_opensearch_urls' $PROPFILE)
+audit_opensearch_protocol=$(get_prop 'audit_opensearch_protocol' $PROPFILE)
+audit_opensearch_port=$(get_prop 'audit_opensearch_port' $PROPFILE)
+audit_opensearch_user=$(get_prop 'audit_opensearch_user' $PROPFILE)
+audit_opensearch_password=$(get_prop 'audit_opensearch_password' $PROPFILE)
+audit_opensearch_index=$(get_prop 'audit_opensearch_index' $PROPFILE)
+audit_opensearch_bootstrap_enabled=$(get_prop
'audit_opensearch_bootstrap_enabled' $PROPFILE)
+audit_opensearch_authentication_type=$(get_prop_or_default
'audit_opensearch_authentication_type' $PROPFILE '')
+audit_opensearch_kerberos_principal=$(get_prop_or_default
'audit_opensearch_kerberos_principal' $PROPFILE '')
+audit_opensearch_kerberos_keytab=$(get_prop_or_default
'audit_opensearch_kerberos_keytab' $PROPFILE '')
audit_solr_urls=$(get_prop 'audit_solr_urls' $PROPFILE)
audit_solr_user=$(get_prop_or_default 'audit_solr_user' $PROPFILE '')
audit_solr_password=$(get_prop_or_default 'audit_solr_password' $PROPFILE '')
@@ -165,6 +175,7 @@ cred_keystore_filename=$(eval echo "$(get_prop
'cred_keystore_filename' $PROPFIL
sso_enabled=$(get_prop 'sso_enabled' $PROPFILE)
sso_providerurl=$(get_prop 'sso_providerurl' $PROPFILE)
sso_publickey=$(get_prop 'sso_publickey' $PROPFILE)
+FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION=$(get_prop_or_default
'FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION' $PROPFILE 'false')
RANGER_ADMIN_LOG_DIR=$(eval echo "$(get_prop 'RANGER_ADMIN_LOG_DIR'
$PROPFILE)")
RANGER_ADMIN_LOGBACK_CONF_FILE=$(eval echo "$(get_prop
'RANGER_ADMIN_LOGBACK_CONF_FILE' $PROPFILE)")
RANGER_PID_DIR_PATH=$(eval echo "$(get_prop 'RANGER_PID_DIR_PATH' $PROPFILE)")
@@ -305,6 +316,16 @@ init_variables(){
exit 1
fi
fi
+ if [ "${audit_store}" == "opensearch" ] ;then
+ if [ "${audit_opensearch_urls}" == "" ] ;then
+ log "[I] Please provide valid URL for 'opensearch'
audit store!"
+ exit 1
+ fi
+ if [ "${audit_opensearch_port}" == "" ] ;then
+ log "[I] Please provide valid port for 'opensearch'
audit store!"
+ exit 1
+ fi
+ fi
if [ "${audit_store}" == "cloudwatch" ] ;then
if [ "${audit_cloudwatch_region}" == "" ] ;then
@@ -857,6 +878,50 @@ update_properties() {
fi
+ if [ "${audit_store}" == "opensearch" ]
+ then
+ propertyName=ranger.audit.opensearch.urls
+ newPropertyValue=${audit_opensearch_urls}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.protocol
+ newPropertyValue=${audit_opensearch_protocol}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.port
+ newPropertyValue=${audit_opensearch_port}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.user
+ newPropertyValue=${audit_opensearch_user}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.password
+ newPropertyValue=${audit_opensearch_password}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.index
+ newPropertyValue=${audit_opensearch_index}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.authentication.type
+ newPropertyValue=${audit_opensearch_authentication_type}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.kerberos.principal
+ newPropertyValue=${audit_opensearch_kerberos_principal}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.kerberos.keytab
+ newPropertyValue=${audit_opensearch_kerberos_keytab}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ propertyName=ranger.audit.opensearch.bootstrap.enabled
+ newPropertyValue=${audit_opensearch_bootstrap_enabled}
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
+ fi
+
if [ "${audit_store}" == "cloudwatch" ]
then
propertyName=ranger.audit.amazon_cloudwatch.region
@@ -1026,6 +1091,16 @@ update_properties() {
updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
fi
+
+ ff_enable_ozone_action_matches_condition=$(echo
"${FF_ENABLE_OZONE_ACTION_MATCHES_CONDITION}" | tr '[:upper:]' '[:lower:]')
+ if [ "${ff_enable_ozone_action_matches_condition}" != "true" ]
+ then
+ ff_enable_ozone_action_matches_condition=false
+ fi
+
propertyName=ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition
+ newPropertyValue="${ff_enable_ozone_action_matches_condition}"
+ updatePropertyToFilePy $propertyName "${newPropertyValue}"
$to_file_ranger
+
if [ "${javax_net_ssl_keyStore}" != "" ] && [
"${javax_net_ssl_keyStorePassword}" != "" ]
then
javax_net_ssl_keyStoreAlias=keyStoreAlias
diff --git
a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefService.java
b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefService.java
index b0ff26134..78e269aa6 100644
---
a/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefService.java
+++
b/security-admin/src/main/java/org/apache/ranger/service/RangerServiceDefService.java
@@ -17,85 +17,200 @@
package org.apache.ranger.service;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
import org.apache.commons.lang3.StringUtils;
import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
import org.apache.ranger.entity.XXServiceDef;
import org.apache.ranger.plugin.model.RangerServiceDef;
+import
org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
@Service
@Scope("singleton")
public class RangerServiceDefService extends
RangerServiceDefServiceBase<XXServiceDef, RangerServiceDef> {
- private final RangerAdminConfig config;
-
- public RangerServiceDefService() {
- super();
-
- this.config = RangerAdminConfig.getInstance();
- }
-
- @Override
- protected void validateForCreate(RangerServiceDef vObj) {
-
- }
-
- @Override
- protected void validateForUpdate(RangerServiceDef vObj, XXServiceDef
entityObj) {
-
- }
-
- @Override
- protected XXServiceDef mapViewToEntityBean(RangerServiceDef vObj,
XXServiceDef xObj, int OPERATION_CONTEXT) {
- return super.mapViewToEntityBean(vObj, xObj, OPERATION_CONTEXT);
- }
-
- @Override
- protected RangerServiceDef mapEntityToViewBean(RangerServiceDef vObj,
XXServiceDef xObj) {
- RangerServiceDef ret = super.mapEntityToViewBean(vObj, xObj);
-
- Map<String, String> serviceDefOptions = ret.getOptions();
-
- if
(serviceDefOptions.get(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)
== null) {
- boolean enableDenyAndExceptionsInPoliciesHiddenOption =
config.getBoolean("ranger.servicedef.enableDenyAndExceptionsInPolicies", true);
- if (enableDenyAndExceptionsInPoliciesHiddenOption ||
StringUtils.equalsIgnoreCase(ret.getName(),
EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME)) {
-
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES,
"true");
- } else {
-
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES,
"false");
- }
- ret.setOptions(serviceDefOptions);
- }
-
- if
(serviceDefOptions.get(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES) ==
null) {
- boolean enableTagBasedPoliciesHiddenOption =
config.getBoolean("ranger.servicedef.enableTagBasedPolicies", true);
- if (enableTagBasedPoliciesHiddenOption) {
-
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES,
"true");
- } else {
-
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES,
"false");
- }
- ret.setOptions(serviceDefOptions);
- }
- return ret;
- }
-
- public List<RangerServiceDef> getAllServiceDefs() {
- List<XXServiceDef> xxServiceDefList = getDao().getAll();
- List<RangerServiceDef> serviceDefList = new
ArrayList<RangerServiceDef>();
-
- for (XXServiceDef xxServiceDef : xxServiceDefList) {
- RangerServiceDef serviceDef =
populateViewBean(xxServiceDef);
- serviceDefList.add(serviceDef);
- }
- return serviceDefList;
- }
-
- public RangerServiceDef getPopulatedViewObject(XXServiceDef
xServiceDef) {
- return this.populateViewBean(xServiceDef);
- }
+ public static final String
PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE =
"ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition";
+ public static final String
OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION =
"enableActionMatcherInPoliciesCondition";
+
+ private static final String POLICY_CONDITION_ACTION_MATCHES =
"action-matches";
+
+ private final RangerAdminConfig config;
+
+ public RangerServiceDefService() {
+ super();
+
+ this.config = RangerAdminConfig.getInstance();
+ }
+
+ public List<RangerServiceDef> getAllServiceDefs() {
+ List<XXServiceDef> xxServiceDefList = getDao().getAll();
+ List<RangerServiceDef> serviceDefList = new ArrayList<>();
+
+ for (XXServiceDef xxServiceDef : xxServiceDefList) {
+ RangerServiceDef serviceDef = populateViewBean(xxServiceDef);
+
+ serviceDefList.add(serviceDef);
+ }
+
+ return serviceDefList;
+ }
+
+ public RangerServiceDef getPopulatedViewObject(XXServiceDef xServiceDef) {
+ return this.populateViewBean(xServiceDef);
+ }
+
+ @Override
+ protected void validateForCreate(RangerServiceDef vObj) {
+ }
+
+ @Override
+ protected void validateForUpdate(RangerServiceDef vObj, XXServiceDef
entityObj) {
+ }
+
+ @Override
+ protected XXServiceDef mapViewToEntityBean(RangerServiceDef vObj,
XXServiceDef xObj, int operationContext) {
+ return super.mapViewToEntityBean(vObj, xObj, operationContext);
+ }
+
+ @Override
+ protected RangerServiceDef mapEntityToViewBean(RangerServiceDef vObj,
XXServiceDef xObj) {
+ RangerServiceDef ret =
super.mapEntityToViewBean(vObj, xObj);
+ Map<String, String> serviceDefOptions = ret.getOptions();
+
+ if
(serviceDefOptions.get(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES)
== null) {
+ boolean enableDenyAndExceptionsInPoliciesHiddenOption =
config.getBoolean("ranger.servicedef.enableDenyAndExceptionsInPolicies", true);
+
+ if (enableDenyAndExceptionsInPoliciesHiddenOption ||
StringUtils.equalsIgnoreCase(ret.getName(),
EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME)) {
+
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES,
"true");
+ } else {
+
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES,
"false");
+ }
+
+ ret.setOptions(serviceDefOptions);
+ }
+
+ if
(serviceDefOptions.get(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES) ==
null) {
+ boolean enableTagBasedPoliciesHiddenOption =
config.getBoolean("ranger.servicedef.enableTagBasedPolicies", true);
+
+ if (enableTagBasedPoliciesHiddenOption) {
+
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES,
"true");
+ } else {
+
serviceDefOptions.put(RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES,
"false");
+ }
+
+ ret.setOptions(serviceDefOptions);
+ }
+
+ return ret;
+ }
+
+ @Override
+ protected RangerServiceDef populateViewBean(XXServiceDef xServiceDef) {
+ final RangerServiceDef ret = super.populateViewBean(xServiceDef);
+
+ applyActionMatcherInPoliciesConditionHiddenOption(ret);
+
+ return ret;
+ }
+
+ void applyActionMatcherInPoliciesConditionHiddenOption(RangerServiceDef
serviceDef) {
+ if (serviceDef == null) {
+ return;
+ }
+
+ final boolean isOzoneServiceDef =
StringUtils.equalsIgnoreCase(serviceDef.getName(),
EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+
+ if (!isOzoneServiceDef) {
+ return;
+ }
+
+ Map<String, String> serviceDefOptions = serviceDef.getOptions();
+
+ if (serviceDefOptions == null) {
+ serviceDefOptions = new HashMap<>();
+ serviceDef.setOptions(serviceDefOptions);
+ }
+
+ // Admin site config is authoritative at runtime; stale def_options
must not override it.
+ boolean enabled =
config.getBoolean(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE,
false);
+
+
serviceDefOptions.put(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
Boolean.toString(enabled));
+ serviceDef.setOptions(serviceDefOptions);
+
+ if (enabled) {
+ ensureActionMatchesPolicyCondition(serviceDef);
+ } else {
+ removeActionMatchesPolicyCondition(serviceDef);
+ }
+ }
+
+ private void ensureActionMatchesPolicyCondition(RangerServiceDef
serviceDef) {
+ if (hasActionMatchesPolicyCondition(serviceDef)) {
+ return;
+ }
+
+ try {
+ final RangerServiceDef embeddedOzoneServiceDef =
EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+
+ if (embeddedOzoneServiceDef == null ||
embeddedOzoneServiceDef.getPolicyConditions() == null) {
+ return;
+ }
+
+ for (RangerPolicyConditionDef embeddedPolicyConditionDef :
embeddedOzoneServiceDef.getPolicyConditions()) {
+ if (StringUtils.equals(embeddedPolicyConditionDef.getName(),
POLICY_CONDITION_ACTION_MATCHES)) {
+ List<RangerPolicyConditionDef> policyConditions =
serviceDef.getPolicyConditions();
+
+ if (policyConditions == null) {
+ policyConditions = new ArrayList<>();
+ serviceDef.setPolicyConditions(policyConditions);
+ }
+
+ policyConditions.add(embeddedPolicyConditionDef);
+
+ return;
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to restore ozone action-matches
policy condition", e);
+ }
+ }
+
+ private void removeActionMatchesPolicyCondition(RangerServiceDef
serviceDef) {
+ List<RangerPolicyConditionDef> policyConditions =
serviceDef.getPolicyConditions();
+
+ if (policyConditions == null || policyConditions.isEmpty()) {
+ return;
+ }
+
+ List<RangerPolicyConditionDef> filteredPolicyConditions = new
ArrayList<>();
+
+ for (RangerPolicyConditionDef policyConditionDef : policyConditions) {
+ if (!StringUtils.equals(policyConditionDef.getName(),
POLICY_CONDITION_ACTION_MATCHES)) {
+ filteredPolicyConditions.add(policyConditionDef);
+ }
+ }
+
+ serviceDef.setPolicyConditions(filteredPolicyConditions);
+ }
+
+ private static boolean hasActionMatchesPolicyCondition(RangerServiceDef
serviceDef) {
+ List<RangerPolicyConditionDef> policyConditions =
serviceDef.getPolicyConditions();
+
+ if (policyConditions == null || policyConditions.isEmpty()) {
+ return false;
+ }
+
+ for (RangerPolicyConditionDef policyConditionDef : policyConditions) {
+ if (StringUtils.equals(policyConditionDef.getName(),
POLICY_CONDITION_ACTION_MATCHES)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
diff --git
a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
index 1af339010..164b63d02 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
@@ -152,6 +152,14 @@
<name>ranger.resource.accessControl.enabled</name>
<value>true</value>
</property>
+ <property>
+
<name>ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition</name>
+ <value>false</value>
+ <description>
+ When true, enables Ozone action-matches
(RangerActionMatcher) policy conditions
+ in the Admin UI and applies S3 action-based evaluation
in the Ozone authorizer.
+ </description>
+ </property>
<property>
<name>ranger.xuser.createdByUserId</name>
<value>1</value>
@@ -322,7 +330,7 @@
<property>
<name>ranger.service.https.attrib.ssl.protocol</name>
- <value>TLSv1.2</value>
+ <value>TLS</value>
</property>
<property>
@@ -516,6 +524,34 @@
<name>ranger.audit.elasticsearch.bootstrap.enabled</name>
<value>true</value>
</property>
+ <property>
+ <name>ranger.audit.opensearch.urls</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.protocol</name>
+ <value>http</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.port</name>
+ <value>9200</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.user</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.password</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.index</name>
+ <value>ranger_audits</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.bootstrap.enabled</name>
+ <value>true</value>
+ </property>
<property>
<name>ranger.audit.solr.max.retry</name>
<value>30</value>
diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
index a75cd05a3..388ce1674 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml
@@ -79,6 +79,49 @@
<name>ranger.audit.elasticsearch.bootstrap.enabled</name>
<value>true</value>
</property>
+ <property>
+ <name>ranger.audit.opensearch.urls</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.protocol</name>
+ <value>http</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.port</name>
+ <value>9200</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.user</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.password</name>
+ <value></value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.index</name>
+ <value>ranger_audits</value>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.authentication.type</name>
+ <value></value>
+ <description>OpenSearch authentication scheme: kerberos, basic,
or none. When left empty it is inferred (kerberos if the password/keytab points
to a keytab file, basic if user and password are set, otherwise
none).</description>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.kerberos.principal</name>
+ <value></value>
+ <description>Kerberos principal used when
authentication.type=kerberos. Falls back to ranger.audit.opensearch.user when
unset.</description>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.kerberos.keytab</name>
+ <value></value>
+ <description>Path to the Kerberos keytab used when
authentication.type=kerberos. Falls back to ranger.audit.opensearch.password
when unset.</description>
+ </property>
+ <property>
+ <name>ranger.audit.opensearch.bootstrap.enabled</name>
+ <value>true</value>
+ </property>
<property>
<name>ranger.audit.amazon_cloudwatch.region</name>
<value>us-east-2</value>
@@ -463,4 +506,8 @@
<name>xasecure.audit.jaas.Client.option.principal</name>
<value></value>
</property>
+ <property>
+
<name>ranger.servicedef.ozone.enableActionMatcherInPoliciesCondition</name>
+ <value>false</value>
+ </property>
</configuration>
diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
index 9182d427e..1fa8e2ec9 100644
--- a/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAUtils.js
@@ -1494,7 +1494,7 @@ export const requestDataTitle = (serviceType) => {
//Policy condition evaluation
export const policyConditionUpdatedJSON = (policyCond) => {
- let newPolicyConditionJSON = [...policyCond];
+ let newPolicyConditionJSON = [...(Array.isArray(policyCond) ? policyCond :
[])];
newPolicyConditionJSON.filter(function (key) {
if (!key?.uiHint || key?.uiHint == "") {
if (
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
index 139592264..b1a02e40f 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/PolicyListing/AddUpdatePolicyForm.jsx
@@ -510,7 +510,7 @@ export default function AddUpdatePolicyForm() {
data.conditions = {};
for (let val of policyData.conditions) {
let conditionObj = find(
- policyConditionUpdatedJSON(serviceCompData?.policyConditions),
+ policyConditionUpdatedJSON(serviceCompData?.policyConditions ||
[]),
function (m) {
if (m.name == val.type) {
return m;
@@ -518,7 +518,9 @@ export default function AddUpdatePolicyForm() {
}
);
- if (!isEmpty(conditionObj.uiHint)) {
+ // Ignore policy conditions that are not defined in the service-def.
+ // Without this guard, form initialization can fail and leave the
page on the loader.
+ if (conditionObj?.uiHint && !isEmpty(conditionObj.uiHint)) {
data.conditions[val?.type] = JSON.parse(conditionObj.uiHint)
.isMultiValue
? val?.values
@@ -700,7 +702,7 @@ export default function AddUpdatePolicyForm() {
for (let data of val.conditions) {
let conditionObj = find(
- policyConditionUpdatedJSON(serviceData?.policyConditions),
+ policyConditionUpdatedJSON(serviceData?.policyConditions || []),
function (m) {
if (m.name == data.type) {
return m;
@@ -708,7 +710,9 @@ export default function AddUpdatePolicyForm() {
}
);
- if (!isEmpty(conditionObj.uiHint)) {
+ // Ignore policy conditions that are not defined in the service-def.
+ // Without this guard, form initialization can fail and leave the
page on the loader.
+ if (conditionObj?.uiHint && !isEmpty(conditionObj.uiHint)) {
obj.conditions[data?.type] = JSON.parse(conditionObj.uiHint)
.isMultiValue
? data?.values.map((m) => {
diff --git
a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java
b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java
index 2d8620d0d..d09f49d51 100644
---
a/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java
+++
b/security-admin/src/test/java/org/apache/ranger/service/TestRangerServiceDefService.java
@@ -19,9 +19,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
import org.apache.ranger.common.ContextUtil;
import org.apache.ranger.common.JSONUtil;
import org.apache.ranger.common.PropertiesUtil;
@@ -38,6 +40,7 @@
import
org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef;
+import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
import org.apache.ranger.plugin.util.ServiceDefUtil;
import org.apache.ranger.security.context.RangerContextHolder;
import org.apache.ranger.security.context.RangerSecurityContext;
@@ -53,13 +56,15 @@
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
+import static
org.apache.ranger.service.RangerServiceDefService.PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE;
import static
org.apache.ranger.service.RangerServiceDefService.PROP_ENABLE_IMPLICIT_CONDITION_EXPRESSION;
@RunWith(MockitoJUnitRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestRangerServiceDefService {
- private static Long Id = 8L;
+ private static final Long Id = 8L;
+ private static final String
OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION =
RangerServiceDefService.OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION;
@InjectMocks
RangerServiceDefService serviceDefService = new
RangerServiceDefService();
@@ -803,4 +808,111 @@ public void testImplicitConditionExpressionDisabled() {
PropertiesUtil.getPropertiesMap().remove(PROP_ENABLE_IMPLICIT_CONDITION_EXPRESSION);
}
}
+
+ @Test
+ public void testOzoneActionPolicyOptionDisabled() {
+
RangerAdminConfig.getInstance().set(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE,
Boolean.FALSE.toString());
+
+ try {
+ RangerServiceDef serviceDef =
buildOzoneServiceDefWithActionMatches();
+
+
serviceDefService.applyActionMatcherInPoliciesConditionHiddenOption(serviceDef);
+
+ Assert.assertEquals("false",
serviceDef.getOptions().get(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION));
+
Assert.assertFalse(hasActionMatchesCondition(serviceDef));
+ } finally {
+
RangerAdminConfig.getInstance().unset(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE);
+ }
+ }
+
+ @Test
+ public void testOzoneActionPolicyOptionEnabled() {
+
RangerAdminConfig.getInstance().set(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE,
Boolean.TRUE.toString());
+
+ try {
+ RangerServiceDef serviceDef =
buildOzoneServiceDefWithActionMatches();
+
+
serviceDefService.applyActionMatcherInPoliciesConditionHiddenOption(serviceDef);
+
+ Assert.assertEquals("true",
serviceDef.getOptions().get(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION));
+
Assert.assertTrue(hasActionMatchesCondition(serviceDef));
+ } finally {
+
RangerAdminConfig.getInstance().unset(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE);
+ }
+ }
+
+ @Test
+ public void testOzoneActionPolicyConfigTrueOverridesStoredFalseOption()
{
+
RangerAdminConfig.getInstance().set(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE,
Boolean.TRUE.toString());
+
+ try {
+ RangerServiceDef serviceDef =
buildOzoneServiceDefWithActionMatches();
+
serviceDef.getOptions().put(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION,
Boolean.FALSE.toString());
+
+
serviceDefService.applyActionMatcherInPoliciesConditionHiddenOption(serviceDef);
+
+ Assert.assertEquals("true",
serviceDef.getOptions().get(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION));
+
Assert.assertTrue(hasActionMatchesCondition(serviceDef));
+ } finally {
+
RangerAdminConfig.getInstance().unset(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE);
+ }
+ }
+
+ @Test
+ public void
testOzoneActionPolicyConfigTrueRestoresMissingActionMatchesCondition() {
+
RangerAdminConfig.getInstance().set(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE,
Boolean.TRUE.toString());
+
+ try {
+ RangerPolicyConditionDef ipRange = new
RangerPolicyConditionDef();
+ ipRange.setName("ip-range");
+
+ List<RangerPolicyConditionDef> policyConditions = new
ArrayList<>();
+ policyConditions.add(ipRange);
+
+ RangerServiceDef serviceDef = new RangerServiceDef();
+
serviceDef.setName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ serviceDef.setOptions(new HashMap<>());
+ serviceDef.setPolicyConditions(policyConditions);
+
+
serviceDefService.applyActionMatcherInPoliciesConditionHiddenOption(serviceDef);
+
+ Assert.assertEquals("true",
serviceDef.getOptions().get(OPTION_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION));
+
Assert.assertTrue(hasActionMatchesCondition(serviceDef));
+ } finally {
+
RangerAdminConfig.getInstance().unset(PROP_ENABLE_ACTION_MATCHER_IN_POLICIES_CONDITION_FOR_OZONE);
+ }
+ }
+
+ private boolean hasActionMatchesCondition(RangerServiceDef serviceDef) {
+ if (serviceDef.getPolicyConditions() == null) {
+ return false;
+ }
+
+ for (RangerPolicyConditionDef conditionDef :
serviceDef.getPolicyConditions()) {
+ if (StringUtils.equals(conditionDef.getName(),
"action-matches")) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private RangerServiceDef buildOzoneServiceDefWithActionMatches() {
+ RangerPolicyConditionDef ipRange = new
RangerPolicyConditionDef();
+ ipRange.setName("ip-range");
+
+ RangerPolicyConditionDef actionMatches = new
RangerPolicyConditionDef();
+ actionMatches.setName("action-matches");
+
+ List<RangerPolicyConditionDef> policyConditions = new
ArrayList<>();
+ policyConditions.add(ipRange);
+ policyConditions.add(actionMatches);
+
+ RangerServiceDef serviceDef = new RangerServiceDef();
+
serviceDef.setName(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_OZONE_NAME);
+ serviceDef.setOptions(new HashMap<>());
+ serviceDef.setPolicyConditions(policyConditions);
+
+ return serviceDef;
+ }
}