1 package com.atlassian.security.auth.trustedapps.filter;
2
3 import java.security.Principal;
4 import java.security.PublicKey;
5
6 import javax.servlet.http.HttpServletRequest;
7 import javax.servlet.http.HttpServletResponse;
8
9 import com.atlassian.security.auth.trustedapps.ApplicationCertificate;
10 import com.atlassian.security.auth.trustedapps.CurrentApplication;
11 import com.atlassian.security.auth.trustedapps.DefaultApplicationCertificate;
12 import com.atlassian.security.auth.trustedapps.EncryptedCertificate;
13 import com.atlassian.security.auth.trustedapps.InvalidCertificateException;
14 import com.atlassian.security.auth.trustedapps.InvalidRemoteAddressException;
15 import com.atlassian.security.auth.trustedapps.InvalidRequestUrlException;
16 import com.atlassian.security.auth.trustedapps.InvalidXForwardedForAddressException;
17 import com.atlassian.security.auth.trustedapps.RequestConditions;
18 import com.atlassian.security.auth.trustedapps.SystemException;
19 import com.atlassian.security.auth.trustedapps.TrustedApplication;
20 import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
21 import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
22 import com.atlassian.security.auth.trustedapps.UserResolver;
23
24 import junit.framework.TestCase;
25
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.when;
28
29 public class TestTrustedAppAuthenticatorImpl extends TestCase
30 {
31 private static final String USER = "blad-de-blah-blah";
32 private final PublicKey publicKey = new PublicKey()
33 {
34 public String getAlgorithm()
35 {
36 return "algy";
37 }
38
39 public byte[] getEncoded()
40 {
41 return new byte[] { 4, 3, 2, 1 };
42 }
43
44 public String getFormat()
45 {
46 return "format";
47 }
48 };
49
50 private final TrustedApplicationsManager manager = new TrustedApplicationsManager()
51 {
52 final CurrentApplication me = new CurrentApplication()
53 {
54 public String getID()
55 {
56 return "me-id";
57 }
58
59 public PublicKey getPublicKey()
60 {
61 return publicKey;
62 }
63
64 public EncryptedCertificate encode(final String userName)
65 {
66 EncryptedCertificate cert = mock(EncryptedCertificate.class);
67 when(cert.getCertificate()).thenReturn(userName);
68 when(cert.getID()).thenReturn("me-id");
69 when(cert.getMagicNumber()).thenReturn("1");
70 when(cert.getProtocolVersion()).thenReturn(Integer.valueOf(1));
71 when(cert.getSecretKey()).thenReturn("secret");
72 return cert;
73 }
74
75 public EncryptedCertificate encode(String userName, String urlToSign)
76 {
77 throw new UnsupportedOperationException();
78 }
79 };
80
81 public CurrentApplication getCurrentApplication()
82 {
83 return me;
84 }
85
86 public TrustedApplication getTrustedApplication(String id)
87 {
88 if (id.equals("me-id"))
89 {
90 return new TrustedApplication()
91 {
92 public ApplicationCertificate decode(EncryptedCertificate certificate, HttpServletRequest request) throws InvalidCertificateException
93 {
94 if (request.getHeader("ip-mismatch") != null)
95 {
96 throw new InvalidCertificateException(new InvalidRemoteAddressException(request.getRemoteAddr()));
97 }
98 if (request.getHeader("forward-mismatch") != null)
99 {
100 throw new InvalidCertificateException(new InvalidXForwardedForAddressException(request.getRemoteAddr()));
101 }
102 if (request.getHeader("url-mismatch") != null)
103 {
104 throw new InvalidCertificateException(new InvalidRequestUrlException(request.getPathInfo()));
105 }
106 if ("bad-cert".equals(certificate.getCertificate()))
107 {
108 throw new InvalidCertificateException(new SystemException("bad-cert", new NullPointerException("what a bad certificate!")));
109 }
110 return new DefaultApplicationCertificate("me-id", certificate.getCertificate(), System.currentTimeMillis());
111 }
112
113 public PublicKey getPublicKey()
114 {
115 return publicKey;
116 }
117
118 public String getID()
119 {
120 return "me-id";
121 }
122
123 public RequestConditions getRequestConditions()
124 {
125 return null;
126 }
127
128 public String getName()
129 {
130 return null;
131 }
132 };
133 }
134 return null;
135 }
136 };
137
138 private final Principal principal = new Principal()
139 {
140 public String getName()
141 {
142 return USER;
143 }
144 };
145
146 private final UserResolver resolver = new UserResolver()
147 {
148 public Principal resolve(ApplicationCertificate certificate)
149 {
150 return (certificate.getUserName().equals(principal.getName())) ? principal : null;
151 };
152 };
153
154 private final boolean[] canLogin = new boolean[] { true };
155 private final AuthenticationController authenticationController = new AuthenticationController()
156 {
157 public boolean shouldAttemptAuthentication(HttpServletRequest request)
158 {
159 return true;
160 }
161
162 public boolean canLogin(Principal user, HttpServletRequest request)
163 {
164 return canLogin[0];
165 }
166 };
167
168 private final Authenticator authenticator = new TrustedApplicationFilterAuthenticator(manager, resolver, authenticationController)
169 {
170 public Result authenticate(HttpServletRequest request, HttpServletResponse response)
171 {
172 return super.authenticate(ImmutableRequest.wrap(request), response);
173 }
174 };
175
176 protected void setUp() throws Exception
177 {
178 canLogin[0] = true;
179 }
180
181
182
183
184
185 public void testKnownAppProtocolVersion0() throws Exception
186 {
187 CurrentApplication me = manager.getCurrentApplication();
188 EncryptedCertificate cert = me.encode(USER);
189 MockRequest request = new MockTrustedAppRequestV0(cert);
190
191 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
192
193 assertNotNull(result);
194 assertEquals(Authenticator.Result.Status.SUCCESS, result.getStatus());
195 }
196
197 public void testKnownAppProtocolVersion1() throws Exception
198 {
199 CurrentApplication me = manager.getCurrentApplication();
200 EncryptedCertificate cert = me.encode(USER);
201 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
202
203 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
204
205 assertNotNull(result);
206 assertEquals(Authenticator.Result.Status.SUCCESS, result.getStatus());
207 assertNotNull(result.getUser());
208 }
209
210 public void testUnknownApp() throws Exception
211 {
212 EncryptedCertificate cert = mock(EncryptedCertificate.class);
213 when(cert.getCertificate()).thenReturn("cert");
214 when(cert.getID()).thenReturn("appId");
215 when(cert.getMagicNumber()).thenReturn("majick");
216 when(cert.getSecretKey()).thenReturn("dis-is-a-sekrit");
217
218 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
219
220
221 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
222
223 assertNotNull(result);
224 assertEquals(Authenticator.Result.Status.FAILED, result.getStatus());
225 assertEquals("APP_UNKNOWN;\tUnknown Application: {0};\t[\"appId\"]", result.getMessage());
226 assertNull(result.getUser());
227 }
228
229 public void testBlankAppId() throws Exception
230 {
231 EncryptedCertificate cert = mock(EncryptedCertificate.class);
232 when(cert.getCertificate()).thenReturn("cert");
233 when(cert.getMagicNumber()).thenReturn("majick");
234 when(cert.getSecretKey()).thenReturn("dis-is-a-sekrit");
235
236 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
237
238
239 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
240
241 assertNotNull(result);
242 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
243 assertEquals("APP_ID_NOT_FOUND;\tApplication ID not found in request;\t[]", result.getMessage());
244 assertNull(result.getUser());
245 }
246
247 public void testBadProtocolVersion() throws Exception
248 {
249 EncryptedCertificate cert = mock(EncryptedCertificate.class);
250 when(cert.getCertificate()).thenReturn("cert");
251 when(cert.getID()).thenReturn("appId");
252 when(cert.getMagicNumber()).thenReturn("majick");
253 when(cert.getSecretKey()).thenReturn("dis-is-a-sekrit");
254
255 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
256 request.addHeader(TrustedApplicationUtils.Header.Request.VERSION, "a-dodgy-version");
257
258 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
259
260 assertNotNull(result);
261 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
262 assertEquals("BAD_PROTOCOL_VERSION;\tBad protocol version: {0};\t[\"a-dodgy-version\"]", result.getMessage());
263 assertNull(result.getUser());
264 }
265
266 public void testMissingSecretKey() throws Exception
267 {
268 EncryptedCertificate cert = mock(EncryptedCertificate.class);
269 when(cert.getCertificate()).thenReturn("cert");
270 when(cert.getID()).thenReturn("appId");
271 when(cert.getMagicNumber()).thenReturn("majick");
272 when(cert.getSecretKey()).thenReturn("");
273
274 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
275
276 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
277
278 assertNotNull(result);
279 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
280 assertEquals("SECRET_KEY_NOT_FOUND;\tSecret Key not found in request;\t[]", result.getMessage());
281 assertNull(result.getUser());
282 }
283
284 public void testMissingMagicNumber() throws Exception
285 {
286 EncryptedCertificate cert = mock(EncryptedCertificate.class);
287 when(cert.getCertificate()).thenReturn("cert");
288 when(cert.getID()).thenReturn("appId");
289 when(cert.getMagicNumber()).thenReturn(null);
290 when(cert.getSecretKey()).thenReturn("dis-is-a-sekrit");
291 when(cert.getProtocolVersion()).thenReturn(Integer.valueOf(1));
292
293 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
294
295 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
296
297 assertNotNull(result);
298 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
299 assertEquals("MAGIC_NUMBER_NOT_FOUND;\tMagic Number not found in request;\t[]", result.getMessage());
300 assertNull(result.getUser());
301 }
302
303 public void testBadCertificate() throws Exception
304 {
305 CurrentApplication me = manager.getCurrentApplication();
306 EncryptedCertificate cert = me.encode(USER);
307 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
308 request.addHeader(TrustedApplicationUtils.Header.Request.CERTIFICATE, "bad-cert");
309
310 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
311
312 assertNotNull(result);
313 assertEquals(result.getMessage(), Authenticator.Result.Status.ERROR, result.getStatus());
314 assertEquals("SYSTEM;\tException: {0} occurred serving request for application: {1};\t[\"java.lang.NullPointerException: what a bad certificate!\",\"bad-cert\"]", result.getMessage());
315 assertNull(result.getUser());
316 }
317
318 public void testPrincipalNotFound() throws Exception
319 {
320 CurrentApplication me = manager.getCurrentApplication();
321 EncryptedCertificate cert = me.encode("unknown-principal");
322 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
323
324 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
325
326 assertNotNull(result);
327 assertEquals(result.getMessage(), Authenticator.Result.Status.FAILED, result.getStatus());
328 assertEquals("USER_UNKNOWN;\tUnknown User: {0};\t[\"unknown-principal\"]", result.getMessage());
329 assertNull(result.getUser());
330 }
331
332 public void testPrincipalLoginDenied() throws Exception
333 {
334 CurrentApplication me = manager.getCurrentApplication();
335 EncryptedCertificate cert = me.encode(USER);
336 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
337
338 canLogin[0] = false;
339 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
340
341 assertNotNull(result);
342 assertEquals(result.getMessage(), Authenticator.Result.Status.FAILED, result.getStatus());
343 assertEquals("PERMISSION_DENIED;\tPermission Denied;\t[]", result.getMessage());
344 assertNull(result.getUser());
345 }
346
347 public void testBadRequestIp() throws Exception
348 {
349 CurrentApplication me = manager.getCurrentApplication();
350 EncryptedCertificate cert = me.encode(USER);
351 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
352 request.addHeader("ip-mismatch", "true");
353
354 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
355
356 assertNotNull(result);
357 assertEquals(result.getMessage(), Authenticator.Result.Status.ERROR, result.getStatus());
358 assertEquals("BAD_REMOTE_IP;\tRequest not allowed from IP address: {0};\t[\"i.am.a.teapot\"]", result.getMessage());
359 assertNull(result.getUser());
360 }
361
362 public void testBadXForwardIp() throws Exception
363 {
364 CurrentApplication me = manager.getCurrentApplication();
365 EncryptedCertificate cert = me.encode(USER);
366 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
367 request.addHeader("forward-mismatch", "true");
368
369 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
370
371 assertNotNull(result);
372 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
373 assertEquals("BAD_XFORWARD_IP;\tRequest not allowed from IP address: {0};\t[\"i.am.a.teapot\"]", result.getMessage());
374 assertNull(result.getUser());
375 }
376
377 public void testBadRequestUrl() throws Exception
378 {
379 CurrentApplication me = manager.getCurrentApplication();
380 EncryptedCertificate cert = me.encode(USER);
381 MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1("/jira/secure/DeleteProject.jspa", cert);
382 request.addHeader("url-mismatch", "true");
383
384 Authenticator.Result result = authenticator.authenticate(request, new MockResponse());
385
386 assertNotNull(result);
387 assertEquals(Authenticator.Result.Status.ERROR, result.getStatus());
388 assertEquals("BAD_URL;\tRequest not allowed to access URL: {0};\t[\"/jira/secure/DeleteProject.jspa\"]", result.getMessage());
389 assertNull(result.getUser());
390 }
391 }