Skip to content

Commit 558281b

Browse files
authored
Change the way ZkClientUriDomainMappingHelper add watch (#111)
ZkClientUriDomainMappingHelper instance is registered as a watcher during the instantiation process, so it will get the notification every time there's a change in the client URI domain mapping, and it can update each session that are connected to this zookeeper server for the updated access control metatdata. However, in ZooKeeper server code, there is an implicit assumption that the objects registered as a watcher are ServerCnxn objects, while ZkClientUriDomainMappingHelper is not an instance of ServerCnxn. This incorrect cast of ZkClientUriDomainMappingHelper to ServerCnxn can lead to code failure. This commit leveraged a dummy server cnxn class `DumbWatcher` which actually functions as a watcher, to prevent this error.
1 parent fef5308 commit 558281b

File tree

2 files changed

+68
-44
lines changed

2 files changed

+68
-44
lines changed

zookeeper-server/src/main/java/org/apache/zookeeper/server/auth/znode/groupacl/ZkClientUriDomainMappingHelper.java

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
import java.util.Set;
2828
import org.apache.zookeeper.KeeperException;
2929
import org.apache.zookeeper.WatchedEvent;
30-
import org.apache.zookeeper.Watcher;
3130
import org.apache.zookeeper.ZooDefs;
31+
import org.apache.zookeeper.server.DumbWatcher;
3232
import org.apache.zookeeper.server.ServerCnxn;
3333
import org.apache.zookeeper.server.ServerCnxnFactory;
3434
import org.apache.zookeeper.server.ZooKeeperServer;
@@ -58,7 +58,7 @@
5858
* Note: It is not expected that there would be too many distinct client URIs so as to overwhelm
5959
* heap usage.
6060
*/
61-
public class ZkClientUriDomainMappingHelper implements Watcher, ClientUriDomainMappingHelper {
61+
public class ZkClientUriDomainMappingHelper implements ClientUriDomainMappingHelper {
6262

6363
private static final Logger LOG = LoggerFactory.getLogger(ZkClientUriDomainMappingHelper.class);
6464

@@ -106,9 +106,13 @@ boolean setDomainAuthUpdater(ConnectionAuthInfoUpdater updater) {
106106

107107
/**
108108
* Install a persistent recursive watch on the root path.
109+
* The watcher has to be added here instead of in {@link org.apache.zookeeper.server.auth.znode.groupacl.X509ZNodeGroupAclProvider}
110+
* because this class is one layer above connections, and is a central place to handle add watch,
111+
* process event, etc logic for all the connections in this server.
112+
* Therefore, only one watch needs to be added per server.
109113
*/
110114
private void addWatches() {
111-
zks.getZKDatabase().addWatch(rootPath, this, ZooDefs.AddWatchModes.persistentRecursive);
115+
zks.getZKDatabase().addWatch(rootPath, new MappingRootWatcher(), ZooDefs.AddWatchModes.persistentRecursive);
112116
}
113117

114118
/**
@@ -141,33 +145,6 @@ private void parseZNodeMapping() {
141145
clientUriToDomainNames = newClientUriToDomainNames;
142146
}
143147

144-
@Override
145-
public void process(WatchedEvent event) {
146-
LOG.info("Processing watched event: {}", event.toString());
147-
parseZNodeMapping();
148-
// Update AuthInfo for all the known connections.
149-
// Note : It is not ideal to iterate over all plaintext connections which are connected over non-TLS but right now
150-
// there is no way to find out if connection on unified port is using SSLHandler or nonSSLHandler. Anyways, we
151-
// should not ideally have any nonSSLHandler connections on unified port after complete rollout.
152-
153-
// TODO Change to read SecureServerCnxnFactory only. The current logic is to support unit test who is not creating
154-
// a secured server cnxn factory. It won't cause any problem but is not technically correct.
155-
156-
// Since port unification is supported, TLS requests could be made on unified as well as secure port. Hence iterate
157-
// over all connections to update auth info.
158-
ServerCnxnFactory factory = zks.getServerCnxnFactory();
159-
LOG.info("Updating auth info for connections");
160-
// TODO Evaluate performance impact and potentially use thread pool to parallelize the AuthInfo update.
161-
if (factory != null) {
162-
factory.getConnections().forEach(cnxn -> updateDomainBasedAuthInfo(cnxn));
163-
}
164-
ServerCnxnFactory secureFactory = zks.getSecureServerCnxnFactory();
165-
LOG.info("Updating auth info for TLS connections");
166-
if (secureFactory != null) {
167-
secureFactory.getConnections().forEach(cnxn -> updateDomainBasedAuthInfo(cnxn));
168-
}
169-
}
170-
171148
@Override
172149
public Set<String> getDomains(String clientUri) {
173150
return clientUriToDomainNames.getOrDefault(clientUri, Collections.emptySet());
@@ -183,4 +160,38 @@ public void updateDomainBasedAuthInfo(ServerCnxn cnxn) {
183160
}
184161
}
185162
}
163+
164+
/**
165+
* The watcher used to listen on client uri - domain mapping root path for mapping update
166+
* Extends DumbWatcher instead of ServerCnxn because there are some package-private methods in
167+
* ServerCnxn which cannot be overridden here, such as {@code abstract void setSessionId(long sessionId);}.
168+
*/
169+
public class MappingRootWatcher extends DumbWatcher {
170+
@Override
171+
public void process(WatchedEvent event) {
172+
LOG.info("Processing watched event: {}", event.toString());
173+
parseZNodeMapping();
174+
// Update AuthInfo for all the known connections.
175+
// Note : It is not ideal to iterate over all plaintext connections which are connected over non-TLS but right now
176+
// there is no way to find out if connection on unified port is using SSLHandler or nonSSLHandler. Anyways, we
177+
// should not ideally have any nonSSLHandler connections on unified port after complete rollout.
178+
179+
// TODO Change to read SecureServerCnxnFactory only. The current logic is to support unit test who is not creating
180+
// a secured server cnxn factory. It won't cause any problem but is not technically correct.
181+
182+
// Since port unification is supported, TLS requests could be made on unified as well as secure port. Hence iterate
183+
// over all connections to update auth info.
184+
ServerCnxnFactory factory = zks.getServerCnxnFactory();
185+
LOG.info("Updating auth info for connections");
186+
// TODO Evaluate performance impact and potentially use thread pool to parallelize the AuthInfo update.
187+
if (factory != null) {
188+
factory.getConnections().forEach(cnxn -> updateDomainBasedAuthInfo(cnxn));
189+
}
190+
ServerCnxnFactory secureFactory = zks.getSecureServerCnxnFactory();
191+
LOG.info("Updating auth info for TLS connections");
192+
if (secureFactory != null) {
193+
secureFactory.getConnections().forEach(cnxn -> updateDomainBasedAuthInfo(cnxn));
194+
}
195+
}
196+
}
186197
}

zookeeper-server/src/test/java/org/apache/zookeeper/server/auth/znode/groupacl/ZkClientUriDomainMappingHelperTest.java

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.zookeeper.server.ServerCnxn;
3333
import org.apache.zookeeper.server.ServerCnxnFactory;
3434
import org.apache.zookeeper.server.ZooKeeperServer;
35+
import org.apache.zookeeper.server.watch.WatchesReport;
3536
import org.apache.zookeeper.test.ClientBase;
3637
import org.junit.After;
3738
import org.junit.Assert;
@@ -122,7 +123,7 @@ public void cleanUp() throws InterruptedException, IOException, KeeperException
122123
* └── bar1 (client URI)
123124
*/
124125
@Test
125-
public void testB_ZkClientUriDomainMappingHelper() throws KeeperException, InterruptedException {
126+
public void testA_ZkClientUriDomainMappingHelper() throws KeeperException, InterruptedException {
126127
for (String path : MAPPING_PATHS) {
127128
zookeeperClientConnection
128129
.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
@@ -137,21 +138,33 @@ public void testB_ZkClientUriDomainMappingHelper() throws KeeperException, Inter
137138
Assert.assertEquals(new HashSet<>(Arrays.asList("bar", "foo")), helper.getDomains("bar1"));
138139

139140
// Add a new application domain and add bar1 to it
140-
zookeeperClientConnection
141-
.create(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new", null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
142-
CreateMode.PERSISTENT);
143-
zookeeperClientConnection.create(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new/bar1", null,
144-
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
145-
146-
// For bar1, we should get bar, foo, and new
147-
Assert
148-
.assertEquals(new HashSet<>(Arrays.asList("bar", "foo", "new")), helper.getDomains("bar1"));
149-
150-
// Remove the application domain and bar1
151-
zookeeperClientConnection.delete(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new/bar1", -1);
152-
zookeeperClientConnection.delete(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new", -1);
141+
try {
142+
zookeeperClientConnection
143+
.create(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new", null, ZooDefs.Ids.OPEN_ACL_UNSAFE,
144+
CreateMode.PERSISTENT);
145+
zookeeperClientConnection.create(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new/bar1", null,
146+
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
147+
148+
// For bar1, we should get bar, foo, and new
149+
Assert.assertEquals(new HashSet<>(Arrays.asList("bar", "foo", "new")), helper.getDomains("bar1"));
150+
} finally {
151+
// Remove the application domain and bar1
152+
zookeeperClientConnection.delete(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new/bar1", -1);
153+
zookeeperClientConnection.delete(CLIENT_URI_DOMAIN_MAPPING_ROOT_PATH + "/new", -1);
154+
}
153155

154156
// For bar1, we should get bar and foo
155157
Assert.assertEquals(new HashSet<>(Arrays.asList("bar", "foo")), helper.getDomains("bar1"));
156158
}
159+
160+
@Test
161+
/**
162+
* Make sure the watcher installed while instantiate ZkClientUriDomainMappingHelper does not break
163+
* the functionality of getting watches
164+
*/
165+
public void testB_GetWatches() {
166+
ClientUriDomainMappingHelper helper = new ZkClientUriDomainMappingHelper(zookeeperServer);
167+
WatchesReport report = zookeeperServer.getZKDatabase().getDataTree().getWatches();
168+
Assert.assertEquals(1, report.getPaths(0).size());
169+
}
157170
}

0 commit comments

Comments
 (0)