1   package com.atlassian.security.auth.trustedapps.filter;
2   
3   import java.security.KeyPair;
4   import java.security.NoSuchAlgorithmException;
5   import java.security.NoSuchProviderException;
6   import java.security.Principal;
7   
8   import javax.servlet.http.HttpServletRequest;
9   
10  import com.atlassian.security.auth.trustedapps.ApplicationCertificate;
11  import com.atlassian.security.auth.trustedapps.BouncyCastleEncryptionProvider;
12  import com.atlassian.security.auth.trustedapps.CurrentApplication;
13  import com.atlassian.security.auth.trustedapps.DefaultCurrentApplication;
14  import com.atlassian.security.auth.trustedapps.DefaultTrustedApplication;
15  import com.atlassian.security.auth.trustedapps.EncryptedCertificate;
16  import com.atlassian.security.auth.trustedapps.EncryptionProvider;
17  import com.atlassian.security.auth.trustedapps.InvalidCertificateException;
18  import com.atlassian.security.auth.trustedapps.InvalidRemoteAddressException;
19  import com.atlassian.security.auth.trustedapps.InvalidRequestException;
20  import com.atlassian.security.auth.trustedapps.InvalidRequestUrlException;
21  import com.atlassian.security.auth.trustedapps.InvalidXForwardedForAddressException;
22  import com.atlassian.security.auth.trustedapps.RequestConditions;
23  import com.atlassian.security.auth.trustedapps.TrustedApplication;
24  import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
25  import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
26  import com.atlassian.security.auth.trustedapps.UserResolver;
27  
28  import junit.framework.TestCase;
29  
30  import static org.mockito.Mockito.mock;
31  import static org.mockito.Mockito.when;
32  
33  /**
34   * This test is really an integration test as it tests the production behaviour of the authenticate method
35   */
36  public class TestTrustedApplicationsFilterAuthenticate extends TestCase
37  {
38      // -----------------------------------------------------------------------------------------------------------------
39      // members
40      // -----------------------------------------------------------------------------------------------------------------
41  
42      private final EncryptionProvider provider = new BouncyCastleEncryptionProvider();
43      private final TrustedApplicationsManager manager = new TrustedApplicationsManager()
44      {
45          final KeyPair pair;
46          final CurrentApplication me;
47  
48          {
49              try
50              {
51                  pair = provider.generateNewKeyPair();
52              }
53              catch (NoSuchAlgorithmException e)
54              {
55                  throw new RuntimeException(e);
56              }
57              catch (NoSuchProviderException e)
58              {
59                  throw new RuntimeException(e);
60              }
61              me = new DefaultCurrentApplication(pair.getPublic(), pair.getPrivate(), "me");
62          }
63  
64          public CurrentApplication getCurrentApplication()
65          {
66              return me;
67          }
68  
69          public TrustedApplication getTrustedApplication(String id)
70          {
71              if (id.equals("me"))
72              {
73                  return new DefaultTrustedApplication(provider, pair.getPublic(), "me", RequestConditions.builder().setCertificateTimeout(1000L).build())
74                  {
75                      @Override
76                      protected void checkRequest(HttpServletRequest request) throws InvalidCertificateException
77                      {
78                          try
79                          {
80                              if (request.getHeader("ip-mismatch") != null)
81                              {
82                                  throw new InvalidRemoteAddressException(request.getRemoteAddr());
83                              }
84                              if (request.getHeader("forward-mismatch") != null)
85                              {
86                                  throw new InvalidXForwardedForAddressException(request.getRemoteAddr());
87                              }
88                              if (request.getHeader("url-mismatch") != null)
89                              {
90                                  throw new InvalidRequestUrlException(request.getPathInfo());
91                              }
92                          }
93                          catch (final InvalidRequestException e)
94                          {
95                              throw new InvalidCertificateException(e);
96                          }
97                      }
98                  };
99              }
100             return null;
101         }
102     };
103 
104     private Principal principal = null;
105     private final UserResolver userResolver = new UserResolver()
106     {
107         public Principal resolve(ApplicationCertificate certificate)
108         {
109             return principal;
110         }
111     };
112     private final TrustedApplicationsFilter filter = new TrustedApplicationsFilter(manager, userResolver, new AuthenticationController()
113     {
114         public boolean canLogin(Principal user, HttpServletRequest request)
115         {
116             return true;
117         }
118 
119         public boolean shouldAttemptAuthentication(HttpServletRequest request)
120         {
121             return true;
122         }
123 
124     }, mock(AuthenticationListener.class));
125 
126     protected void setUp() throws Exception
127     {
128         principal = null;
129     }
130 
131     // -----------------------------------------------------------------------------------------------------------------
132     // tests
133     // -----------------------------------------------------------------------------------------------------------------
134 
135     public void testNoTrustedAppAttempt() throws Exception
136     {
137         MockRequest request = new MockRequest("/some/path");
138         MockResponse response = new MockResponse();
139         assertFalse(filter.authenticate(request, response));
140         // TODO: add assertion about listener
141         final String error = (String) response.getHeaders().get(TrustedApplicationUtils.Header.Response.ERROR);
142         assertNull(error, error);
143     }
144 
145     public void testKnownAppProtocolVersion0() throws Exception
146     {
147         CurrentApplication me = manager.getCurrentApplication();
148         principal = new Principal()
149         {
150             public String getName()
151             {
152                 return "blah-de-blah-blah";
153             }
154         };
155         EncryptedCertificate cert = me.encode("blah-de-blah-blah");
156         MockRequest request = new MockTrustedAppRequestV0(cert);
157         MockResponse response = new MockResponse();
158         assertTrue(filter.authenticate(request, response));
159         // TODO: add assertion about listener
160         final String error = (String) response.getHeaders().get(TrustedApplicationUtils.Header.Response.ERROR);
161         assertNull(error, error);
162     }
163 
164     public void testKnownAppProtocolVersion1() throws Exception
165     {
166         CurrentApplication me = manager.getCurrentApplication();
167         principal = new Principal()
168         {
169             public String getName()
170             {
171                 return "blah-de-blah-blah";
172             }
173         };
174         EncryptedCertificate cert = me.encode("blah-de-blah-blah");
175         MockRequest request = new MockTrustedAppRequestV1(cert);
176 
177         MockResponse response = new MockResponse();
178         assertTrue(filter.authenticate(request, response));
179         // TODO: add assertion about listener
180         final String error = (String) response.getHeaders().get(TrustedApplicationUtils.Header.Response.ERROR);
181         assertNull(error, error);
182     }
183 
184     public void testUnknownApp() throws Exception
185     {
186         EncryptedCertificate cert = mock(EncryptedCertificate.class);
187         when(cert.getCertificate()).thenReturn("cert");
188         when(cert.getID()).thenReturn("appId");
189         when(cert.getMagicNumber()).thenReturn("majick");
190         when(cert.getSecretKey()).thenReturn("dis-is-a-sekrit");
191         
192         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
193         assertFailed("Unknown Application:", request);
194     }
195 
196     public void testBadSecretKey() throws Exception
197     {
198         CurrentApplication me = manager.getCurrentApplication();
199         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
200         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
201         request.addHeader(TrustedApplicationUtils.Header.Request.SECRET_KEY, "0123981237123827234842374");
202         assertError("BAD_MAGIC;\tUnable to decrypt certificate {0} for application {1};\t[\"secret key\",\"me\"]", request);
203     }
204 
205     public void testBadCertificate() throws Exception
206     {
207         CurrentApplication me = manager.getCurrentApplication();
208         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
209         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
210         request.addHeader(TrustedApplicationUtils.Header.Request.CERTIFICATE, "0123981237123827234842374");
211         assertError("BAD_MAGIC;\tUnable to decrypt certificate {0} for application {1};\t[\"secret key\",\"me\"]", request);
212     }
213 
214     public void testBadPublicKey() throws Exception
215     {
216         CurrentApplication me = manager.getCurrentApplication();
217         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
218         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
219         request.addHeader(TrustedApplicationUtils.Header.Request.MAGIC, "123798211233723187217");
220         assertError("BAD_MAGIC;\tUnable to decrypt certificate {0} for application {1};\t[\"public key\",\"me\"]", request);
221     }
222 
223     public void testBadRequestIp() throws Exception
224     {
225         CurrentApplication me = manager.getCurrentApplication();
226         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
227         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
228         request.addHeader("ip-mismatch", "true");
229         assertError("BAD_REMOTE_IP;\tRequest not allowed from IP address: {0};\t[\"i.am.a.teapot\"]", request);
230     }
231 
232     public void testBadXForwardIp() throws Exception
233     {
234         CurrentApplication me = manager.getCurrentApplication();
235         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
236         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1(cert);
237         request.addHeader("forward-mismatch", "true");
238         assertError("BAD_XFORWARD_IP;\tRequest not allowed from IP address: {0};\t[\"i.am.a.teapot\"]", request);
239     }
240 
241     public void testBadRequestUrl() throws Exception
242     {
243         CurrentApplication me = manager.getCurrentApplication();
244         EncryptedCertificate cert = me.encode("blad-de-blah-blah");
245         MockTrustedAppRequestV1 request = new MockTrustedAppRequestV1("/jira/secure/DeleteProject.jspa", cert);
246         request.addHeader("url-mismatch", "true");
247         assertError("BAD_URL;\tRequest not allowed to access URL: {0};\t[\"/jira/secure/DeleteProject.jspa\"]", request);
248     }
249 
250     // -----------------------------------------------------------------------------------------------------------------
251     // helpers
252     // -----------------------------------------------------------------------------------------------------------------
253 
254     private void assertFailed(String msg, MockRequest request)
255     {
256         MockResponse response = new MockResponse();
257 
258         assertFalse(filter.authenticate(request, response));
259         // TODO: add assertion about listener
260         final String error = (String) response.getHeaders().get(TrustedApplicationUtils.Header.Response.ERROR);
261         System.out.println(error);
262         assertNotNull(error);
263         assertTrue("Expected: '" + msg + "' got:" + error, error.indexOf(msg) >= 0);
264     }
265 
266     private void assertError(String msg, MockRequest request)
267     {
268         MockResponse response = new MockResponse();
269 
270         assertFalse(filter.authenticate(request, response));
271         // TODO: add assertion about listener       
272         final String error = (String) response.getHeaders().get(TrustedApplicationUtils.Header.Response.ERROR);
273         System.out.println(error);
274         assertNotNull(error);
275         assertEquals(msg, error);
276         //assertTrue("Expected: '" + msg + "' got:" + error, error.indexOf(msg) >= 0);
277     }
278 }