PageRenderTime 64ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/core/src/test/scala/unit/kafka/security/auth/ZkAuthorizationTest.scala

https://gitlab.com/JHUDevOpsFall2016/kafka-alex
Scala | 321 lines | 214 code | 22 blank | 85 comment | 15 complexity | 6aaf5d7f878dc13a9501bf26509ff22a MD5 | raw file
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package kafka.security.auth
  18. import kafka.admin.ZkSecurityMigrator
  19. import kafka.utils.{Logging, ZkUtils}
  20. import kafka.zk.ZooKeeperTestHarness
  21. import org.apache.kafka.common.KafkaException
  22. import org.apache.kafka.common.security.JaasUtils
  23. import org.apache.zookeeper.data.{ACL}
  24. import org.junit.Assert._
  25. import org.junit.{After, Before, Test}
  26. import scala.collection.JavaConverters._
  27. import scala.util.{Try, Success, Failure}
  28. import javax.security.auth.login.Configuration
  29. class ZkAuthorizationTest extends ZooKeeperTestHarness with Logging {
  30. val jaasFile = kafka.utils.JaasTestUtils.writeZkFile
  31. val authProvider = "zookeeper.authProvider.1"
  32. @Before
  33. override def setUp() {
  34. Configuration.setConfiguration(null)
  35. System.setProperty(JaasUtils.JAVA_LOGIN_CONFIG_PARAM, jaasFile)
  36. System.setProperty(authProvider, "org.apache.zookeeper.server.auth.SASLAuthenticationProvider")
  37. super.setUp()
  38. }
  39. @After
  40. override def tearDown() {
  41. super.tearDown()
  42. System.clearProperty(JaasUtils.JAVA_LOGIN_CONFIG_PARAM)
  43. System.clearProperty(authProvider)
  44. Configuration.setConfiguration(null)
  45. }
  46. /**
  47. * Tests the method in JaasUtils that checks whether to use
  48. * secure ACLs and authentication with ZooKeeper.
  49. */
  50. @Test
  51. def testIsZkSecurityEnabled() {
  52. assertTrue(JaasUtils.isZkSecurityEnabled())
  53. Configuration.setConfiguration(null)
  54. System.clearProperty(JaasUtils.JAVA_LOGIN_CONFIG_PARAM)
  55. assertFalse(JaasUtils.isZkSecurityEnabled())
  56. try {
  57. Configuration.setConfiguration(null)
  58. System.setProperty(JaasUtils.JAVA_LOGIN_CONFIG_PARAM, "no-such-file-exists.conf")
  59. JaasUtils.isZkSecurityEnabled()
  60. fail("Should have thrown an exception")
  61. } catch {
  62. case e: KafkaException => // Expected
  63. }
  64. }
  65. /**
  66. * Exercises the code in ZkUtils. The goal is mainly
  67. * to verify that the behavior of ZkUtils is correct
  68. * when isSecure is set to true.
  69. */
  70. @Test
  71. def testZkUtils() {
  72. assertTrue(zkUtils.isSecure)
  73. for (path <- zkUtils.persistentZkPaths) {
  74. zkUtils.makeSurePersistentPathExists(path)
  75. if(!path.equals(ZkUtils.ConsumersPath)) {
  76. val aclList = zkUtils.zkConnection.getAcl(path).getKey
  77. assertTrue(aclList.size == 2)
  78. for (acl: ACL <- aclList.asScala) {
  79. assertTrue(isAclSecure(acl))
  80. }
  81. }
  82. }
  83. // Test that can create: createEphemeralPathExpectConflict
  84. zkUtils.createEphemeralPathExpectConflict("/a", "")
  85. verify("/a")
  86. // Test that can create: createPersistentPath
  87. zkUtils.createPersistentPath("/b")
  88. verify("/b")
  89. // Test that can create: createSequentialPersistentPath
  90. val seqPath = zkUtils.createSequentialPersistentPath("/c", "")
  91. verify(seqPath)
  92. // Test that can update: updateEphemeralPath
  93. zkUtils.updateEphemeralPath("/a", "updated")
  94. val valueA: String = zkUtils.zkClient.readData("/a")
  95. assertTrue(valueA.equals("updated"))
  96. // Test that can update: updatePersistentPath
  97. zkUtils.updatePersistentPath("/b", "updated")
  98. val valueB: String = zkUtils.zkClient.readData("/b")
  99. assertTrue(valueB.equals("updated"))
  100. info("Leaving testZkUtils")
  101. }
  102. /**
  103. * Tests the migration tool when making an unsecure
  104. * cluster secure.
  105. */
  106. @Test
  107. def testZkMigration() {
  108. val unsecureZkUtils = ZkUtils(zkConnect, 6000, 6000, false)
  109. try {
  110. testMigration(zkConnect, unsecureZkUtils, zkUtils)
  111. } finally {
  112. unsecureZkUtils.close()
  113. }
  114. }
  115. /**
  116. * Tests the migration tool when making a secure
  117. * cluster unsecure.
  118. */
  119. @Test
  120. def testZkAntiMigration() {
  121. val unsecureZkUtils = ZkUtils(zkConnect, 6000, 6000, false)
  122. try {
  123. testMigration(zkConnect, zkUtils, unsecureZkUtils)
  124. } finally {
  125. unsecureZkUtils.close()
  126. }
  127. }
  128. /**
  129. * Tests that the persistent paths cannot be deleted.
  130. */
  131. @Test
  132. def testDelete() {
  133. info(s"zkConnect string: $zkConnect")
  134. ZkSecurityMigrator.run(Array("--zookeeper.acl=secure", s"--zookeeper.connect=$zkConnect"))
  135. deleteAllUnsecure()
  136. }
  137. /**
  138. * Tests that znodes cannot be deleted when the
  139. * persistent paths have children.
  140. */
  141. @Test
  142. def testDeleteRecursive() {
  143. info(s"zkConnect string: $zkConnect")
  144. for (path <- zkUtils.securePersistentZkPaths) {
  145. info(s"Creating $path")
  146. zkUtils.makeSurePersistentPathExists(path)
  147. zkUtils.createPersistentPath(s"$path/fpjwashere", "")
  148. }
  149. zkUtils.zkConnection.setAcl("/", zkUtils.DefaultAcls, -1)
  150. deleteAllUnsecure()
  151. }
  152. /**
  153. * Tests the migration tool when chroot is being used.
  154. */
  155. @Test
  156. def testChroot {
  157. val zkUrl = zkConnect + "/kafka"
  158. zkUtils.createPersistentPath("/kafka")
  159. val unsecureZkUtils = ZkUtils(zkUrl, 6000, 6000, false)
  160. val secureZkUtils = ZkUtils(zkUrl, 6000, 6000, true)
  161. try {
  162. testMigration(zkUrl, unsecureZkUtils, secureZkUtils)
  163. } finally {
  164. unsecureZkUtils.close()
  165. secureZkUtils.close()
  166. }
  167. }
  168. /**
  169. * Exercises the migration tool. It is used in these test cases:
  170. * testZkMigration, testZkAntiMigration, testChroot.
  171. */
  172. private def testMigration(zkUrl: String, firstZk: ZkUtils, secondZk: ZkUtils) {
  173. info(s"zkConnect string: $zkUrl")
  174. for (path <- firstZk.securePersistentZkPaths) {
  175. info(s"Creating $path")
  176. firstZk.makeSurePersistentPathExists(path)
  177. // Create a child for each znode to exercise the recurrent
  178. // traversal of the data tree
  179. firstZk.createPersistentPath(s"$path/fpjwashere", "")
  180. }
  181. // Getting security option to determine how to verify ACLs.
  182. // Additionally, we create the consumers znode (not in
  183. // securePersistentZkPaths) to make sure that we don't
  184. // add ACLs to it.
  185. val secureOpt: String =
  186. if (secondZk.isSecure) {
  187. firstZk.createPersistentPath(ZkUtils.ConsumersPath)
  188. "secure"
  189. } else {
  190. secondZk.createPersistentPath(ZkUtils.ConsumersPath)
  191. "unsecure"
  192. }
  193. ZkSecurityMigrator.run(Array(s"--zookeeper.acl=$secureOpt", s"--zookeeper.connect=$zkUrl"))
  194. info("Done with migration")
  195. for (path <- secondZk.securePersistentZkPaths) {
  196. val listParent = secondZk.zkConnection.getAcl(path).getKey
  197. assertTrue(path, isAclCorrect(listParent, secondZk.isSecure))
  198. val childPath = path + "/fpjwashere"
  199. val listChild = secondZk.zkConnection.getAcl(childPath).getKey
  200. assertTrue(childPath, isAclCorrect(listChild, secondZk.isSecure))
  201. }
  202. // Check consumers path.
  203. val consumersAcl = firstZk.zkConnection.getAcl(ZkUtils.ConsumersPath).getKey
  204. assertTrue(ZkUtils.ConsumersPath, isAclCorrect(consumersAcl, false))
  205. }
  206. /**
  207. * Verifies that the path has the appropriate secure ACL.
  208. */
  209. private def verify(path: String): Boolean = {
  210. val list = zkUtils.zkConnection.getAcl(path).getKey
  211. list.asScala.forall(isAclSecure)
  212. }
  213. /**
  214. * Verifies ACL.
  215. */
  216. private def isAclCorrect(list: java.util.List[ACL], secure: Boolean): Boolean = {
  217. val isListSizeCorrect =
  218. if (secure)
  219. list.size == 2
  220. else
  221. list.size == 1
  222. isListSizeCorrect && list.asScala.forall(
  223. if (secure)
  224. isAclSecure
  225. else
  226. isAclUnsecure
  227. )
  228. }
  229. /**
  230. * Verifies that this ACL is the secure one. The
  231. * values are based on the constants used in the
  232. * ZooKeeper code base.
  233. */
  234. private def isAclSecure(acl: ACL): Boolean = {
  235. info(s"ACL $acl")
  236. acl.getPerms match {
  237. case 1 => acl.getId.getScheme.equals("world")
  238. case 31 => acl.getId.getScheme.equals("sasl")
  239. case _ => false
  240. }
  241. }
  242. /**
  243. * Verifies that the ACL corresponds to the unsecure one.
  244. */
  245. private def isAclUnsecure(acl: ACL): Boolean = {
  246. info(s"ACL $acl")
  247. acl.getPerms match {
  248. case 31 => acl.getId.getScheme.equals("world")
  249. case _ => false
  250. }
  251. }
  252. /**
  253. * Sets up and starts the recursive execution of deletes.
  254. * This is used in the testDelete and testDeleteRecursive
  255. * test cases.
  256. */
  257. private def deleteAllUnsecure() {
  258. System.setProperty(JaasUtils.ZK_SASL_CLIENT, "false")
  259. val unsecureZkUtils = ZkUtils(zkConnect, 6000, 6000, false)
  260. val result: Try[Boolean] = {
  261. deleteRecursive(unsecureZkUtils, "/")
  262. }
  263. // Clean up before leaving the test case
  264. unsecureZkUtils.close()
  265. System.clearProperty(JaasUtils.ZK_SASL_CLIENT)
  266. // Fail the test if able to delete
  267. result match {
  268. case Success(v) => // All done
  269. case Failure(e) => fail(e.getMessage)
  270. }
  271. }
  272. /**
  273. * Tries to delete znodes recursively
  274. */
  275. private def deleteRecursive(zkUtils: ZkUtils, path: String): Try[Boolean] = {
  276. info(s"Deleting $path")
  277. var result: Try[Boolean] = Success(true)
  278. for (child <- zkUtils.getChildren(path))
  279. result = (path match {
  280. case "/" => deleteRecursive(zkUtils, s"/$child")
  281. case path => deleteRecursive(zkUtils, s"$path/$child")
  282. }) match {
  283. case Success(v) => result
  284. case Failure(e) => Failure(e)
  285. }
  286. path match {
  287. // Do not try to delete the root
  288. case "/" => result
  289. // For all other paths, try to delete it
  290. case path =>
  291. try {
  292. zkUtils.deletePath(path)
  293. Failure(new Exception(s"Have been able to delete $path"))
  294. } catch {
  295. case e: Exception => result
  296. }
  297. }
  298. }
  299. }