Skip to content

Commit 4fb3011

Browse files
vyommanimneethiraj
andauthored
RANGER-5394: policyEngine should be volatile to prevent policy update… (#729)
* RANGER-5394: policyEngine should be volatile to prevent policy update visibility race * addressed the review comments and make other fields also volatile * Update RangerBasePlugin.java --------- Co-authored-by: Madhan Neethiraj <[email protected]>
1 parent 7008e02 commit 4fb3011

File tree

2 files changed

+198
-15
lines changed

2 files changed

+198
-15
lines changed

agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,23 @@
9090
public class RangerBasePlugin {
9191
private static final Logger LOG = LoggerFactory.getLogger(RangerBasePlugin.class);
9292

93-
private final RangerPluginConfig pluginConfig;
94-
private final RangerPluginContext pluginContext;
95-
private final Map<String, LogHistory> logHistoryList = new Hashtable<>();
96-
private final int logInterval = 30000; // 30 seconds
97-
private final DownloadTrigger accessTrigger = new DownloadTrigger();
98-
private final List<RangerChainedPlugin> chainedPlugins;
99-
private final boolean dedupStrings;
100-
private PolicyRefresher refresher;
101-
private RangerPolicyEngine policyEngine;
102-
private RangerAuthContext currentAuthContext;
103-
private RangerAccessResultProcessor resultProcessor;
104-
private RangerRoles roles;
105-
private boolean isUserStoreEnricherAddedImplcitly;
106-
private Map<String, String> serviceConfigs;
107-
private boolean synchronousPolicyRefresh;
93+
private final RangerPluginConfig pluginConfig;
94+
private final RangerPluginContext pluginContext;
95+
private final Map<String, LogHistory> logHistoryList = new Hashtable<>();
96+
private final int logInterval = 30000; // 30 seconds
97+
private final DownloadTrigger accessTrigger = new DownloadTrigger();
98+
private final List<RangerChainedPlugin> chainedPlugins;
99+
private final boolean dedupStrings;
100+
101+
private volatile RangerPolicyEngine policyEngine;
102+
private volatile RangerAuthContext currentAuthContext;
103+
private volatile RangerRoles roles;
104+
private volatile Map<String, String> serviceConfigs;
105+
106+
private PolicyRefresher refresher;
107+
private RangerAccessResultProcessor resultProcessor;
108+
private boolean isUserStoreEnricherAddedImplcitly;
109+
private boolean synchronousPolicyRefresh;
108110

109111
public RangerBasePlugin(String serviceType, String appId) {
110112
this(new RangerPluginConfig(serviceType, null, appId, null, null, null));
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.ranger.plugin.service;
21+
22+
import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
23+
import org.apache.ranger.plugin.model.RangerPolicy;
24+
import org.apache.ranger.plugin.model.RangerServiceDef;
25+
import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
26+
import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
27+
import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
28+
import org.apache.ranger.plugin.policyengine.RangerAccessResult;
29+
import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions;
30+
import org.apache.ranger.plugin.util.ServicePolicies;
31+
import org.junit.Before;
32+
import org.junit.Test;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
35+
36+
import java.util.ArrayList;
37+
import java.util.Collections;
38+
import java.util.HashMap;
39+
import java.util.List;
40+
import java.util.Map;
41+
import java.util.concurrent.CountDownLatch;
42+
import java.util.concurrent.TimeUnit;
43+
import java.util.concurrent.atomic.AtomicInteger;
44+
45+
import static org.junit.Assert.fail;
46+
47+
public class TestRangerBasePluginRaceCondition {
48+
private static final Logger LOG = LoggerFactory.getLogger(TestRangerBasePluginRaceCondition.class);
49+
50+
private static final String SERVICE_TYPE = "hive";
51+
private static final String SERVICE_NAME = "test-hive";
52+
private static final String APP_ID = "test-app";
53+
private static final String USER = "bob";
54+
private static final int NUM_WORKERS = 10;
55+
private RangerBasePlugin plugin;
56+
private RangerAccessRequest request;
57+
58+
@Before
59+
public void setUp() {
60+
System.setProperty("ranger.plugin.trino.policy.pollIntervalMs", "-1");
61+
System.setProperty("ranger.plugin.trino.policy.source.impl",
62+
"org.apache.ranger.plugin.service.TestRangerBasePluginRaceCondition$MockRangerAdminClient");
63+
System.setProperty("ranger.plugin.trino.policy.deltas", "false");
64+
System.setProperty("ranger.plugin.trino.in.place.policy.updates", "false");
65+
RangerPolicyEngineOptions peOptions = new RangerPolicyEngineOptions();
66+
RangerPluginConfig pluginConfig = new RangerPluginConfig(SERVICE_TYPE, SERVICE_NAME, APP_ID, "cl1", "on-perm", peOptions);
67+
pluginConfig.set("ranger.plugin.hive.policy.rest.url", "http://dummy:1234");
68+
69+
plugin = new RangerBasePlugin(pluginConfig);
70+
plugin.init();
71+
request = createAccessRequest(USER, "table1", "select");
72+
}
73+
74+
@Test
75+
public void testVisibilityOfPolicyEngine() throws Exception {
76+
plugin.setPolicies(createServicePolicies(true, 15_000, 1L));
77+
78+
CountDownLatch ready = new CountDownLatch(NUM_WORKERS);
79+
CountDownLatch go = new CountDownLatch(1);
80+
CountDownLatch done = new CountDownLatch(NUM_WORKERS);
81+
AtomicInteger sawDeny = new AtomicInteger(0);
82+
83+
for (int i = 0; i < NUM_WORKERS; i++) {
84+
int id = i;
85+
new Thread(() -> {
86+
ready.countDown();
87+
try {
88+
go.await();
89+
} catch (Exception ignored) {
90+
}
91+
boolean denied = false;
92+
for (int j = 0; j < 100_000_000; j++) {
93+
RangerAccessResult r = plugin.isAccessAllowed(request);
94+
if (r != null && !r.getIsAllowed()) {
95+
sawDeny.incrementAndGet();
96+
denied = true;
97+
LOG.info("Worker-{} SAW DENY at {}.", id, j);
98+
break;
99+
}
100+
}
101+
if (!denied) {
102+
LOG.info("Worker-{} NEVER SAW DENY!", id);
103+
}
104+
done.countDown();
105+
}).start();
106+
}
107+
ready.await();
108+
109+
Thread updater = new Thread(() -> {
110+
LOG.info("UPDATING POLICY...");
111+
long start = System.nanoTime();
112+
plugin.setPolicies(createServicePolicies(false, 18_000, 2L));
113+
long duration = (System.nanoTime() - start) / 1_000_000;
114+
LOG.info("UPDATE TOOK {} ms", duration);
115+
});
116+
updater.start();
117+
go.countDown();
118+
updater.join();
119+
done.await(60, TimeUnit.SECONDS);
120+
int count = sawDeny.get();
121+
if (count < NUM_WORKERS) {
122+
fail("RACE! Only " + count + "/" + NUM_WORKERS + " saw DENY!");
123+
}
124+
}
125+
126+
private RangerAccessRequest createAccessRequest(String user, String table, String accessType) {
127+
RangerAccessRequestImpl request = new RangerAccessRequestImpl();
128+
Map<String, Object> resource = new HashMap<>();
129+
resource.put("table", table);
130+
request.setResource(new RangerAccessResourceImpl(resource));
131+
request.setUser(user);
132+
request.setAccessType(accessType);
133+
request.setAction(accessType);
134+
return request;
135+
}
136+
137+
private ServicePolicies createServicePolicies(boolean allowBob, int policyCount, long version) {
138+
ServicePolicies servicePolicies = new ServicePolicies();
139+
servicePolicies.setServiceName(SERVICE_NAME);
140+
servicePolicies.setServiceDef(createServiceDef());
141+
servicePolicies.setPolicyVersion(version);
142+
List<RangerPolicy> policies = new ArrayList<>(policyCount);
143+
144+
for (int i = 0; i < policyCount; i++) {
145+
RangerPolicy p = new RangerPolicy();
146+
p.setId(1000L + i);
147+
p.setName("policy-" + i + "-v" + version);
148+
p.setService(SERVICE_NAME);
149+
p.setIsEnabled(true);
150+
151+
Map<String, RangerPolicy.RangerPolicyResource> resMap = new HashMap<>();
152+
RangerPolicy.RangerPolicyResource res = new RangerPolicy.RangerPolicyResource();
153+
res.setValue("table1");
154+
res.setValues(Collections.singletonList("table1"));
155+
resMap.put("table", res);
156+
p.setResources(resMap);
157+
158+
RangerPolicy.RangerPolicyItem item = new RangerPolicy.RangerPolicyItem();
159+
item.setUsers(Collections.singletonList(USER));
160+
RangerPolicy.RangerPolicyItemAccess acc = new RangerPolicy.RangerPolicyItemAccess();
161+
acc.setType("select");
162+
acc.setIsAllowed(allowBob);
163+
item.setAccesses(Collections.singletonList(acc));
164+
165+
if (allowBob) {
166+
p.setPolicyItems(Collections.singletonList(item));
167+
} else {
168+
p.setDenyPolicyItems(Collections.singletonList(item));
169+
}
170+
policies.add(p);
171+
}
172+
servicePolicies.setPolicies(policies);
173+
return servicePolicies;
174+
}
175+
176+
private RangerServiceDef createServiceDef() {
177+
RangerServiceDef def = new RangerServiceDef();
178+
def.setName(SERVICE_TYPE);
179+
return def;
180+
}
181+
}

0 commit comments

Comments
 (0)