PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/Source/JNA/waffle-tests/src/test/java/waffle/servlet/NegotiateSecurityFilterTests.java

http://github.com/dblock/waffle
Java | 415 lines | 275 code | 28 blank | 112 comment | 12 complexity | 85c54c8de2554261e443309d2eef0344 MD5 | raw file
Possible License(s): MIT, MPL-2.0-no-copyleft-exception, Apache-2.0, LGPL-3.0, LGPL-2.1, GPL-2.0
  1. /**
  2. * Waffle (https://github.com/Waffle/waffle)
  3. *
  4. * Copyright (c) 2010-2018 Application Security, Inc.
  5. *
  6. * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
  7. * Public License v1.0 which accompanies this distribution, and is available at
  8. * https://www.eclipse.org/legal/epl-v10.html.
  9. *
  10. * Contributors: Application Security, Inc.
  11. */
  12. package waffle.servlet;
  13. import static org.assertj.core.api.Assertions.assertThat;
  14. import com.sun.jna.platform.win32.Advapi32Util;
  15. import com.sun.jna.platform.win32.Secur32.EXTENDED_NAME_FORMAT;
  16. import com.sun.jna.platform.win32.Secur32Util;
  17. import com.sun.jna.platform.win32.Sspi;
  18. import com.sun.jna.platform.win32.SspiUtil.ManagedSecBufferDesc;
  19. import java.io.IOException;
  20. import java.util.ArrayList;
  21. import java.util.Base64;
  22. import javax.security.auth.Subject;
  23. import javax.servlet.ServletException;
  24. import org.junit.jupiter.api.AfterEach;
  25. import org.junit.jupiter.api.Assertions;
  26. import org.junit.jupiter.api.BeforeEach;
  27. import org.junit.jupiter.api.Test;
  28. import waffle.mock.MockWindowsAuthProvider;
  29. import waffle.mock.MockWindowsIdentity;
  30. import waffle.mock.http.SimpleFilterChain;
  31. import waffle.mock.http.SimpleFilterConfig;
  32. import waffle.mock.http.SimpleHttpRequest;
  33. import waffle.mock.http.SimpleHttpResponse;
  34. import waffle.windows.auth.IWindowsCredentialsHandle;
  35. import waffle.windows.auth.PrincipalFormat;
  36. import waffle.windows.auth.impl.WindowsAccountImpl;
  37. import waffle.windows.auth.impl.WindowsAuthProviderImpl;
  38. import waffle.windows.auth.impl.WindowsCredentialsHandleImpl;
  39. import waffle.windows.auth.impl.WindowsSecurityContextImpl;
  40. /**
  41. * Waffle Tomcat Security Filter Tests.
  42. *
  43. * @author dblock[at]dblock[dot]org
  44. */
  45. public class NegotiateSecurityFilterTests {
  46. /** The Constant NEGOTIATE. */
  47. private static final String NEGOTIATE = "Negotiate";
  48. /** The Constant NTLM. */
  49. private static final String NTLM = "NTLM";
  50. /** The filter. */
  51. private NegotiateSecurityFilter filter;
  52. /**
  53. * Sets the up.
  54. *
  55. * @throws ServletException
  56. * the servlet exception
  57. */
  58. @BeforeEach
  59. public void setUp() throws ServletException {
  60. this.filter = new NegotiateSecurityFilter();
  61. this.filter.setAuth(new WindowsAuthProviderImpl());
  62. this.filter.init(null);
  63. }
  64. /**
  65. * Tear down.
  66. */
  67. @AfterEach
  68. public void tearDown() {
  69. this.filter.destroy();
  70. }
  71. /**
  72. * Test challenge get.
  73. *
  74. * @throws IOException
  75. * Signals that an I/O exception has occurred.
  76. * @throws ServletException
  77. * the servlet exception
  78. */
  79. @Test
  80. public void testChallengeGET() throws IOException, ServletException {
  81. final SimpleHttpRequest request = new SimpleHttpRequest();
  82. request.setMethod("GET");
  83. final SimpleHttpResponse response = new SimpleHttpResponse();
  84. this.filter.doFilter(request, response, null);
  85. final String[] wwwAuthenticates = response.getHeaderValues("WWW-Authenticate");
  86. Assertions.assertEquals(3, wwwAuthenticates.length);
  87. Assertions.assertEquals(NegotiateSecurityFilterTests.NEGOTIATE, wwwAuthenticates[0]);
  88. Assertions.assertEquals(NegotiateSecurityFilterTests.NTLM, wwwAuthenticates[1]);
  89. Assertions.assertTrue(wwwAuthenticates[2].startsWith("Basic realm=\""));
  90. Assertions.assertEquals(2, response.getHeaderNamesSize());
  91. Assertions.assertEquals("keep-alive", response.getHeader("Connection"));
  92. Assertions.assertEquals(401, response.getStatus());
  93. }
  94. /**
  95. * Test challenge post.
  96. *
  97. * @throws IOException
  98. * Signals that an I/O exception has occurred.
  99. * @throws ServletException
  100. * the servlet exception
  101. */
  102. @Test
  103. public void testChallengePOST() throws IOException, ServletException {
  104. final String securityPackage = NegotiateSecurityFilterTests.NEGOTIATE;
  105. IWindowsCredentialsHandle clientCredentials = null;
  106. WindowsSecurityContextImpl clientContext = null;
  107. try {
  108. // client credentials handle
  109. clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
  110. clientCredentials.initialize();
  111. // initial client security context
  112. clientContext = new WindowsSecurityContextImpl();
  113. clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
  114. clientContext.setCredentialsHandle(clientCredentials);
  115. clientContext.setSecurityPackage(securityPackage);
  116. clientContext.initialize(null, null, WindowsAccountImpl.getCurrentUsername());
  117. final SimpleHttpRequest request = new SimpleHttpRequest();
  118. request.setMethod("POST");
  119. request.setContentLength(0);
  120. final String clientToken = Base64.getEncoder().encodeToString(clientContext.getToken());
  121. request.addHeader("Authorization", securityPackage + " " + clientToken);
  122. final SimpleHttpResponse response = new SimpleHttpResponse();
  123. this.filter.doFilter(request, response, null);
  124. Assertions.assertTrue(response.getHeader("WWW-Authenticate").startsWith(securityPackage + " "));
  125. Assertions.assertEquals("keep-alive", response.getHeader("Connection"));
  126. Assertions.assertEquals(2, response.getHeaderNamesSize());
  127. Assertions.assertEquals(401, response.getStatus());
  128. } finally {
  129. if (clientContext != null) {
  130. clientContext.dispose();
  131. }
  132. if (clientCredentials != null) {
  133. clientCredentials.dispose();
  134. }
  135. }
  136. }
  137. /**
  138. * Test negotiate.
  139. *
  140. * @throws IOException
  141. * Signals that an I/O exception has occurred.
  142. * @throws ServletException
  143. * the servlet exception
  144. */
  145. @Test
  146. public void testNegotiate() throws IOException, ServletException {
  147. final String securityPackage = NegotiateSecurityFilterTests.NEGOTIATE;
  148. // client credentials handle
  149. IWindowsCredentialsHandle clientCredentials = null;
  150. WindowsSecurityContextImpl clientContext = null;
  151. // role will contain both Everyone and SID
  152. this.filter.setRoleFormat("both");
  153. try {
  154. // client credentials handle
  155. clientCredentials = WindowsCredentialsHandleImpl.getCurrent(securityPackage);
  156. clientCredentials.initialize();
  157. // initial client security context
  158. clientContext = new WindowsSecurityContextImpl();
  159. clientContext.setPrincipalName(WindowsAccountImpl.getCurrentUsername());
  160. clientContext.setCredentialsHandle(clientCredentials);
  161. clientContext.setSecurityPackage(securityPackage);
  162. clientContext.initialize(null, null, WindowsAccountImpl.getCurrentUsername());
  163. // filter chain
  164. final SimpleFilterChain filterChain = new SimpleFilterChain();
  165. // negotiate
  166. boolean authenticated = false;
  167. final SimpleHttpRequest request = new SimpleHttpRequest();
  168. while (true) {
  169. final String clientToken = Base64.getEncoder().encodeToString(clientContext.getToken());
  170. request.addHeader("Authorization", securityPackage + " " + clientToken);
  171. final SimpleHttpResponse response = new SimpleHttpResponse();
  172. this.filter.doFilter(request, response, filterChain);
  173. final Subject subject = (Subject) request.getSession(false).getAttribute("javax.security.auth.subject");
  174. authenticated = subject != null && subject.getPrincipals().size() > 0;
  175. if (authenticated) {
  176. assertThat(response.getHeaderNamesSize()).isGreaterThanOrEqualTo(0);
  177. break;
  178. }
  179. Assertions.assertTrue(response.getHeader("WWW-Authenticate").startsWith(securityPackage + " "));
  180. Assertions.assertEquals("keep-alive", response.getHeader("Connection"));
  181. Assertions.assertEquals(2, response.getHeaderNamesSize());
  182. Assertions.assertEquals(401, response.getStatus());
  183. final String continueToken = response.getHeader("WWW-Authenticate")
  184. .substring(securityPackage.length() + 1);
  185. final byte[] continueTokenBytes = Base64.getDecoder().decode(continueToken);
  186. assertThat(continueTokenBytes.length).isGreaterThan(0);
  187. final ManagedSecBufferDesc continueTokenBuffer = new ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN,
  188. continueTokenBytes);
  189. clientContext.initialize(clientContext.getHandle(), continueTokenBuffer, "localhost");
  190. }
  191. Assertions.assertTrue(authenticated);
  192. Assertions.assertTrue(filterChain.getRequest() instanceof NegotiateRequestWrapper);
  193. Assertions.assertTrue(filterChain.getResponse() instanceof SimpleHttpResponse);
  194. final NegotiateRequestWrapper wrappedRequest = (NegotiateRequestWrapper) filterChain.getRequest();
  195. Assertions.assertEquals(NegotiateSecurityFilterTests.NEGOTIATE.toUpperCase(), wrappedRequest.getAuthType());
  196. Assertions.assertEquals(Secur32Util.getUserNameEx(EXTENDED_NAME_FORMAT.NameSamCompatible),
  197. wrappedRequest.getRemoteUser());
  198. Assertions.assertTrue(wrappedRequest.getUserPrincipal() instanceof WindowsPrincipal);
  199. final String everyoneGroupName = Advapi32Util.getAccountBySid("S-1-1-0").name;
  200. Assertions.assertTrue(wrappedRequest.isUserInRole(everyoneGroupName));
  201. Assertions.assertTrue(wrappedRequest.isUserInRole("S-1-1-0"));
  202. } finally {
  203. if (clientContext != null) {
  204. clientContext.dispose();
  205. }
  206. if (clientCredentials != null) {
  207. clientCredentials.dispose();
  208. }
  209. }
  210. }
  211. /**
  212. * Test negotiate previous auth with windows principal.
  213. *
  214. * @throws IOException
  215. * Signals that an I/O exception has occurred.
  216. * @throws ServletException
  217. * the servlet exception
  218. */
  219. @Test
  220. public void testNegotiatePreviousAuthWithWindowsPrincipal() throws IOException, ServletException {
  221. final MockWindowsIdentity mockWindowsIdentity = new MockWindowsIdentity("user", new ArrayList<String>());
  222. final SimpleHttpRequest request = new SimpleHttpRequest();
  223. final WindowsPrincipal windowsPrincipal = new WindowsPrincipal(mockWindowsIdentity);
  224. request.setUserPrincipal(windowsPrincipal);
  225. final SimpleFilterChain filterChain = new SimpleFilterChain();
  226. final SimpleHttpResponse response = new SimpleHttpResponse();
  227. this.filter.doFilter(request, response, filterChain);
  228. Assertions.assertTrue(filterChain.getRequest() instanceof NegotiateRequestWrapper);
  229. final NegotiateRequestWrapper wrappedRequest = (NegotiateRequestWrapper) filterChain.getRequest();
  230. Assertions.assertTrue(wrappedRequest.getUserPrincipal() instanceof WindowsPrincipal);
  231. Assertions.assertEquals(windowsPrincipal, wrappedRequest.getUserPrincipal());
  232. }
  233. /**
  234. * Test challenge ntlmpost.
  235. *
  236. * @throws IOException
  237. * Signals that an I/O exception has occurred.
  238. * @throws ServletException
  239. * the servlet exception
  240. */
  241. @Test
  242. public void testChallengeNTLMPOST() throws IOException, ServletException {
  243. final MockWindowsIdentity mockWindowsIdentity = new MockWindowsIdentity("user", new ArrayList<String>());
  244. final SimpleHttpRequest request = new SimpleHttpRequest();
  245. final WindowsPrincipal windowsPrincipal = new WindowsPrincipal(mockWindowsIdentity);
  246. request.setUserPrincipal(windowsPrincipal);
  247. request.setMethod("POST");
  248. request.setContentLength(0);
  249. request.addHeader("Authorization", "NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==");
  250. final SimpleFilterChain filterChain = new SimpleFilterChain();
  251. final SimpleHttpResponse response = new SimpleHttpResponse();
  252. this.filter.doFilter(request, response, filterChain);
  253. Assertions.assertEquals(401, response.getStatus());
  254. final String[] wwwAuthenticates = response.getHeaderValues("WWW-Authenticate");
  255. Assertions.assertEquals(1, wwwAuthenticates.length);
  256. Assertions.assertTrue(wwwAuthenticates[0].startsWith("NTLM "));
  257. Assertions.assertEquals(2, response.getHeaderNamesSize());
  258. Assertions.assertEquals("keep-alive", response.getHeader("Connection"));
  259. Assertions.assertEquals(401, response.getStatus());
  260. }
  261. /**
  262. * Test challenge ntlmput.
  263. *
  264. * @throws IOException
  265. * Signals that an I/O exception has occurred.
  266. * @throws ServletException
  267. * the servlet exception
  268. */
  269. @Test
  270. public void testChallengeNTLMPUT() throws IOException, ServletException {
  271. final MockWindowsIdentity mockWindowsIdentity = new MockWindowsIdentity("user", new ArrayList<String>());
  272. final SimpleHttpRequest request = new SimpleHttpRequest();
  273. final WindowsPrincipal windowsPrincipal = new WindowsPrincipal(mockWindowsIdentity);
  274. request.setUserPrincipal(windowsPrincipal);
  275. request.setMethod("PUT");
  276. request.setContentLength(0);
  277. request.addHeader("Authorization", "NTLM TlRMTVNTUAABAAAABzIAAAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==");
  278. final SimpleFilterChain filterChain = new SimpleFilterChain();
  279. final SimpleHttpResponse response = new SimpleHttpResponse();
  280. this.filter.doFilter(request, response, filterChain);
  281. Assertions.assertEquals(401, response.getStatus());
  282. final String[] wwwAuthenticates = response.getHeaderValues("WWW-Authenticate");
  283. Assertions.assertEquals(1, wwwAuthenticates.length);
  284. Assertions.assertTrue(wwwAuthenticates[0].startsWith("NTLM "));
  285. Assertions.assertEquals(2, response.getHeaderNamesSize());
  286. Assertions.assertEquals("keep-alive", response.getHeader("Connection"));
  287. Assertions.assertEquals(401, response.getStatus());
  288. }
  289. /**
  290. * Test init basic security filter provider.
  291. *
  292. * @throws ServletException
  293. * the servlet exception
  294. */
  295. @Test
  296. public void testInitBasicSecurityFilterProvider() throws ServletException {
  297. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  298. filterConfig.setParameter("principalFormat", "sid");
  299. filterConfig.setParameter("roleFormat", "none");
  300. filterConfig.setParameter("allowGuestLogin", "true");
  301. filterConfig.setParameter("securityFilterProviders", "waffle.servlet.spi.BasicSecurityFilterProvider\n");
  302. filterConfig.setParameter("waffle.servlet.spi.BasicSecurityFilterProvider/realm", "DemoRealm");
  303. filterConfig.setParameter("authProvider", MockWindowsAuthProvider.class.getName());
  304. this.filter.init(filterConfig);
  305. Assertions.assertEquals(this.filter.getPrincipalFormat(), PrincipalFormat.SID);
  306. Assertions.assertEquals(this.filter.getRoleFormat(), PrincipalFormat.NONE);
  307. Assertions.assertTrue(this.filter.isAllowGuestLogin());
  308. Assertions.assertEquals(1, this.filter.getProviders().size());
  309. Assertions.assertTrue(this.filter.getAuth() instanceof MockWindowsAuthProvider);
  310. }
  311. /**
  312. * Test init two security filter providers.
  313. *
  314. * @throws ServletException
  315. * the servlet exception
  316. */
  317. @Test
  318. public void testInitTwoSecurityFilterProviders() throws ServletException {
  319. // make sure that providers can be specified separated by any kind of space
  320. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  321. filterConfig.setParameter("securityFilterProviders", "waffle.servlet.spi.BasicSecurityFilterProvider\n"
  322. + "waffle.servlet.spi.NegotiateSecurityFilterProvider waffle.servlet.spi.BasicSecurityFilterProvider");
  323. this.filter.init(filterConfig);
  324. Assertions.assertEquals(3, this.filter.getProviders().size());
  325. }
  326. /**
  327. * Test init negotiate security filter provider.
  328. *
  329. * @throws ServletException
  330. * the servlet exception
  331. */
  332. @Test
  333. public void testInitNegotiateSecurityFilterProvider() throws ServletException {
  334. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  335. filterConfig.setParameter("securityFilterProviders", "waffle.servlet.spi.NegotiateSecurityFilterProvider\n");
  336. filterConfig.setParameter("waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols",
  337. "NTLM\nNegotiate NTLM");
  338. this.filter.init(filterConfig);
  339. Assertions.assertEquals(this.filter.getPrincipalFormat(), PrincipalFormat.FQN);
  340. Assertions.assertEquals(this.filter.getRoleFormat(), PrincipalFormat.FQN);
  341. Assertions.assertTrue(this.filter.isAllowGuestLogin());
  342. Assertions.assertEquals(1, this.filter.getProviders().size());
  343. }
  344. /**
  345. * Test init negotiate security filter provider invalid protocol.
  346. */
  347. @Test
  348. public void testInitNegotiateSecurityFilterProviderInvalidProtocol() {
  349. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  350. filterConfig.setParameter("securityFilterProviders", "waffle.servlet.spi.NegotiateSecurityFilterProvider\n");
  351. filterConfig.setParameter("waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols", "INVALID");
  352. try {
  353. this.filter.init(filterConfig);
  354. Assertions.fail("expected ServletException");
  355. } catch (final ServletException e) {
  356. Assertions.assertEquals("java.lang.RuntimeException: Unsupported protocol: INVALID", e.getMessage());
  357. }
  358. }
  359. /**
  360. * Test init invalid parameter.
  361. */
  362. @Test
  363. public void testInitInvalidParameter() {
  364. try {
  365. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  366. filterConfig.setParameter("invalidParameter", "random");
  367. this.filter.init(filterConfig);
  368. Assertions.fail("expected ServletException");
  369. } catch (final ServletException e) {
  370. Assertions.assertEquals("Invalid parameter: invalidParameter", e.getMessage());
  371. }
  372. }
  373. /**
  374. * Test init invalid class in parameter.
  375. */
  376. @Test
  377. public void testInitInvalidClassInParameter() {
  378. try {
  379. final SimpleFilterConfig filterConfig = new SimpleFilterConfig();
  380. filterConfig.setParameter("invalidClass/invalidParameter", "random");
  381. this.filter.init(filterConfig);
  382. Assertions.fail("expected ServletException");
  383. } catch (final ServletException e) {
  384. Assertions.assertEquals("java.lang.ClassNotFoundException: invalidClass", e.getMessage());
  385. }
  386. }
  387. }