1 package com.atlassian.security.auth.trustedapps.filter;
2
3 import java.security.KeyPair;
4 import java.security.KeyPairGenerator;
5 import java.security.NoSuchAlgorithmException;
6 import java.security.Principal;
7
8 import javax.servlet.http.HttpServletRequest;
9 import javax.servlet.http.HttpServletResponse;
10
11 import com.atlassian.security.auth.trustedapps.ApplicationCertificate;
12 import com.atlassian.security.auth.trustedapps.BouncyCastleEncryptionProvider;
13 import com.atlassian.security.auth.trustedapps.DefaultTrustedApplication;
14 import com.atlassian.security.auth.trustedapps.EncryptedCertificate;
15 import com.atlassian.security.auth.trustedapps.EncryptionProvider;
16 import com.atlassian.security.auth.trustedapps.RequestConditions;
17 import com.atlassian.security.auth.trustedapps.TrustedApplication;
18 import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
19 import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
20 import com.atlassian.security.auth.trustedapps.UserResolver;
21 import com.atlassian.security.auth.trustedapps.filter.Authenticator.Result;
22 import com.atlassian.security.auth.trustedapps.request.TrustedRequest;
23 import com.atlassian.security.auth.trustedapps.request.commonshttpclient.CommonsHttpClientTrustedRequest;
24
25 import org.apache.commons.httpclient.Header;
26 import org.apache.commons.httpclient.HttpMethod;
27 import org.apache.commons.httpclient.URIException;
28 import org.apache.commons.httpclient.methods.GetMethod;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.mockito.Mock;
33 import org.mockito.Mockito;
34 import org.mockito.invocation.InvocationOnMock;
35 import org.mockito.runners.MockitoJUnitRunner;
36 import org.mockito.stubbing.Answer;
37
38 import static org.junit.Assert.assertEquals;
39 import static org.junit.Assert.assertNotNull;
40 import static org.junit.Assert.assertNull;
41 import static org.mockito.Mockito.mock;
42 import static org.mockito.Mockito.when;
43
44
45
46
47
48 @RunWith(MockitoJUnitRunner.class)
49 public class TestAuthenticationIntegration
50 {
51
52 private KeyPair keyPair;
53
54
55 @Mock public TrustedApplicationsManager appManager;
56 @Mock public AuthenticationController authenticationController;
57 @Mock public TrustedApplication trustedApplication;
58
59 @Before
60 public void cryptoAndTrustedAppsMockBehaviour() throws NoSuchAlgorithmException
61 {
62 keyPair = generateSingleUseKeyPair();
63
64 RequestConditions conditions = RequestConditions.builder().build();
65 trustedApplication = new DefaultTrustedApplication(keyPair.getPublic(), "appId", conditions);
66
67 when(appManager.getTrustedApplication("appId")).thenReturn(trustedApplication);
68 ApplicationCertificate cert = mock(ApplicationCertificate.class);
69 when(cert.getUserName()).thenReturn("User");
70
71 when(authenticationController.canLogin(Mockito.<Principal>anyObject(), Mockito.<HttpServletRequest>anyObject())).thenReturn(true);
72 }
73
74 public static KeyPair generateSingleUseKeyPair() throws NoSuchAlgorithmException
75 {
76 KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
77 return kpg.genKeyPair();
78 }
79
80 private EncryptedCertificate getEncryptedCertificate(String urlToSign)
81 {
82 EncryptionProvider provider;
83 provider = new BouncyCastleEncryptionProvider();
84 EncryptedCertificate certificate = provider.createEncryptedCertificate("user", keyPair.getPrivate(), "appId", urlToSign);
85 return certificate;
86 }
87
88 public HttpServletRequest mockHttpServletRequestWithFieldsFromHttpMethod(HttpMethod method)
89 throws URIException
90 {
91 HttpServletRequest hrq = mock(HttpServletRequest.class);
92
93 final String requestUrl = method.getURI().toString();
94
95 when(hrq.getRequestURL()).thenAnswer(new Answer<StringBuffer>()
96 {
97 public StringBuffer answer(InvocationOnMock invocation) throws Throwable
98 {
99 return new StringBuffer(requestUrl);
100 }
101 });
102
103 String[] toMock = {
104 "ID",
105 "Cert",
106 "Key",
107 "Version",
108 "Magic",
109 "Signature"
110 };
111
112 for (String f : toMock)
113 {
114 String hn = "X-Seraph-Trusted-App-" + f;
115 Header header = method.getRequestHeader(hn);
116 if (header != null) {
117 when(hrq.getHeader(hn)).thenReturn(header.getValue());
118 }
119 }
120
121 return hrq;
122 }
123
124 public HttpMethod requestFor(String url) throws Exception
125 {
126 HttpMethod method = new GetMethod(url);
127 TrustedRequest request = new CommonsHttpClientTrustedRequest(method);
128 TrustedApplicationUtils.addRequestParameters(getEncryptedCertificate(url), request);
129 return method;
130 }
131
132 @Test
133 public void validRequestIsAccepted() throws Exception
134 {
135
136 DummyResolver resolver = new DummyResolver();
137
138 HttpMethod method = requestFor("http://www.example.com/");
139
140 TrustedApplicationFilterAuthenticator authenticator = new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
141
142 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
143
144
145 HttpServletResponse response = mock(HttpServletResponse.class);
146 Result result = authenticator.authenticate(hrq, response);
147 assertEquals("success", ((Object) result.getStatus()).toString());
148 assertEquals("Trusted Apps enlowercases usernames", "appId/user", result.getUser().getName());
149 }
150
151 @Test
152 public void errorWhenNoTrustedAppsHeadersArePresent()
153 {
154 UserResolver resolver = null;
155 TrustedApplicationFilterAuthenticator authenticator = new TrustedApplicationFilterAuthenticator(appManager, resolver , authenticationController);
156
157 HttpServletRequest hrq = mock(HttpServletRequest.class);
158
159 HttpServletResponse response = mock(HttpServletResponse.class);
160 Result result = authenticator.authenticate(hrq, response);
161 assertEquals("no attempt", ((Object) result.getStatus()).toString());
162 }
163
164 @Test
165 public void errorWhenKeyIsModified() throws Exception
166 {
167 HttpMethod method = requestFor("http://www.example.com/");
168
169 method.setRequestHeader("X-Seraph-Trusted-App-Key", "XXXX");
170
171 TrustedApplicationFilterAuthenticator authenticator = new TrustedApplicationFilterAuthenticator(appManager, null, authenticationController);
172
173 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
174
175
176 HttpServletResponse response = mock(HttpServletResponse.class);
177 Result result = authenticator.authenticate(hrq, response);
178 assertEquals("error", ((Object) result.getStatus()).toString());
179 }
180
181 @Test
182 public void errorWhenUserIsUnknown() throws Exception
183 {
184 UserResolver resolver = mock(UserResolver.class);
185
186 HttpMethod method = requestFor("http://www.example.com/");
187
188 TrustedApplicationFilterAuthenticator authenticator = new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
189
190 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
191
192
193 HttpServletResponse response = mock(HttpServletResponse.class);
194 Result result = authenticator.authenticate(hrq, response);
195 assertEquals("failed", ((Object) result.getStatus()).toString());
196 }
197
198 @Test
199 public void noSignedUrlWhenSignatureIsNotProvided() throws Exception
200 {
201
202 DummyResolver resolver = new DummyResolver();
203
204 HttpMethod method = requestFor("http://www.example.com/");
205 method.setRequestHeader("X-Seraph-Trusted-App-Version", "1");
206 method.removeRequestHeader("X-Seraph-Trusted-App-Signature");
207
208 TrustedApplicationFilterAuthenticator authenticator = new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
209
210 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
211
212
213 HttpServletResponse response = mock(HttpServletResponse.class);
214 Result result = authenticator.authenticate(hrq, response);
215 assertEquals("success", ((Object) result.getStatus()).toString());
216 assertNull("No signed URL as no signature provided", ((Result.Success) result).getSignedUrl());
217 }
218
219 @Test
220 public void errorWhenSignatureIsInvalid() throws Exception
221 {
222
223 DummyResolver resolver = new DummyResolver();
224
225 HttpMethod method = requestFor("http://www.example.com/");
226
227 TrustedApplicationFilterAuthenticator authenticator =
228 new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
229
230 method.setRequestHeader("X-Seraph-Trusted-App-Signature", "XXXX");
231 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
232
233
234 HttpServletResponse response = mock(HttpServletResponse.class);
235 Result result = authenticator.authenticate(hrq, response);
236 assertEquals("error", ((Object) result.getStatus()).toString());
237 }
238
239 @Test
240 public void signedUrlIncludedInResultWhenSignatureIsValid() throws Exception
241 {
242
243 DummyResolver resolver = new DummyResolver();
244
245 HttpMethod method = requestFor("http://www.example.com/");
246
247 TrustedApplicationFilterAuthenticator authenticator =
248 new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
249
250 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
251 assertNotNull(method.getRequestHeader("X-Seraph-Trusted-App-Signature"));
252
253 HttpServletResponse response = mock(HttpServletResponse.class);
254 Result result = authenticator.authenticate(hrq, response);
255 assertEquals("success", ((Object) result.getStatus()).toString());
256 assertEquals("http://www.example.com/", ((Result.Success) result).getSignedUrl());
257 }
258
259 @Test
260 public void signingADifferentUrlCausesAnError() throws Exception
261 {
262
263 DummyResolver resolver = new DummyResolver();
264
265 HttpMethod method = requestFor("http://www.example.com/");
266
267 TrustedApplicationFilterAuthenticator authenticator =
268 new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
269
270 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
271 when(hrq.getRequestURL()).thenAnswer(new Answer<StringBuffer>(){
272 public StringBuffer answer(InvocationOnMock invocation) throws Throwable
273 {
274 return new StringBuffer("http://unexpected.hostname.invalid/");
275 }
276 });
277 assertNotNull(method.getRequestHeader("X-Seraph-Trusted-App-Signature"));
278
279 HttpServletResponse response = mock(HttpServletResponse.class);
280 Result result = authenticator.authenticate(hrq, response);
281 assertEquals("error", ((Object) result.getStatus()).toString());
282 }
283
284 @Test
285 public void v2RequestWithoutSignatureIsAnError() throws Exception
286 {
287 DummyResolver resolver = new DummyResolver();
288
289 HttpMethod method = requestFor("http://www.example.com/");
290 assertEquals("2", method.getRequestHeader("X-Seraph-Trusted-App-Version").getValue());
291 method.removeRequestHeader("X-Seraph-Trusted-App-Signature");
292
293 TrustedApplicationFilterAuthenticator authenticator =
294 new TrustedApplicationFilterAuthenticator(appManager, resolver, authenticationController);
295
296 HttpServletRequest hrq = mockHttpServletRequestWithFieldsFromHttpMethod(method);
297
298 HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
299 Result result = authenticator.authenticate(hrq, response);
300 assertEquals("error", ((Object) result.getStatus()).toString());
301 }
302
303 static class DummyResolver implements UserResolver
304 {
305 public Principal resolve(ApplicationCertificate certificate)
306 {
307 Principal p = mock(Principal.class);
308 when(p.getName()).thenReturn(certificate.getApplicationID() + "/" + certificate.getUserName());
309 return p;
310 }
311 }
312 }