1 package com.atlassian.seraph.filter;
2
3 import com.atlassian.security.auth.trustedapps.ApplicationCertificate;
4 import com.atlassian.security.auth.trustedapps.CurrentApplication;
5 import com.atlassian.security.auth.trustedapps.DefaultEncryptedCertificate;
6 import com.atlassian.security.auth.trustedapps.InvalidCertificateException;
7 import com.atlassian.security.auth.trustedapps.TransportErrorMessage;
8 import com.atlassian.security.auth.trustedapps.TrustedApplication;
9 import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
10 import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
11 import com.atlassian.security.auth.trustedapps.UserResolver;
12 import com.atlassian.seraph.auth.DefaultAuthenticator;
13 import com.atlassian.seraph.auth.RoleMapper;
14 import com.atlassian.seraph.config.SecurityConfigFactory;
15
16 import org.apache.log4j.Logger;
17 import org.bouncycastle.util.encoders.Base64;
18
19 import java.io.IOException;
20 import java.io.OutputStreamWriter;
21 import java.io.UnsupportedEncodingException;
22 import java.io.Writer;
23 import java.security.Principal;
24 import java.security.PublicKey;
25
26 import javax.servlet.Filter;
27 import javax.servlet.FilterChain;
28 import javax.servlet.FilterConfig;
29 import javax.servlet.ServletException;
30 import javax.servlet.ServletRequest;
31 import javax.servlet.ServletResponse;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public class TrustedApplicationsFilter implements Filter
54 {
55 private static final Logger log = Logger.getLogger(TrustedApplicationsFilter.class);
56
57 private static final class Status
58 {
59 static final String ERROR = "ERROR";
60 static final String OK = "OK";
61 }
62
63 private final CertificateServer certificateServer;
64 private final Authenticator authenticator;
65
66 private FilterConfig filterConfig = null;
67
68
69
70
71
72 public TrustedApplicationsFilter(TrustedApplicationsManager appManager, UserResolver resolver)
73 {
74 this(appManager, resolver, SecurityConfigFactory.getInstance().getRoleMapper());
75 }
76
77
78
79 public TrustedApplicationsFilter(TrustedApplicationsManager appManager, UserResolver resolver, RoleMapper roleMapper)
80 {
81 this(new CertificateServerImpl(appManager), new AuthenticatorImpl(appManager, resolver, roleMapper));
82 }
83
84 TrustedApplicationsFilter(CertificateServer certificateServer, Authenticator authenticator)
85 {
86 notNull("certificateServer", certificateServer);
87 notNull("authenticator", authenticator);
88 this.certificateServer = certificateServer;
89 this.authenticator = authenticator;
90 }
91
92 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
93 {
94
95
96 HttpServletRequest request = (HttpServletRequest) req;
97 HttpServletResponse response = (HttpServletResponse) res;
98
99 if (getPathInfo(request).endsWith("/admin/appTrustCertificate"))
100 {
101 response.setContentType("text/plain");
102 certificateServer.writeCertificate(new OutputStreamWriter(response.getOutputStream()));
103 return;
104 }
105
106 boolean isTrustedAppCall = false;
107
108 if (request.getAttribute(BaseLoginFilter.OS_AUTHSTATUS_KEY) == null)
109 {
110 String status = authenticate((HttpServletRequest) req, (HttpServletResponse) res);
111 if (BaseLoginFilter.LOGIN_SUCCESS.equals(status))
112 {
113 request.setAttribute(BaseLoginFilter.OS_AUTHSTATUS_KEY, status);
114 response.setHeader(TrustedApplicationUtils.Header.Response.STATUS, Status.OK);
115 isTrustedAppCall = true;
116 }
117 }
118 try
119 {
120 chain.doFilter(request, res);
121 }
122 finally
123 {
124 if (isTrustedAppCall && request.getSession(false) != null)
125 {
126 request.getSession().invalidate();
127 }
128 }
129 }
130
131 public String authenticate(HttpServletRequest request, HttpServletResponse response)
132 {
133 final Authenticator.Result result = authenticator.authenticate(request);
134
135 switch (result.getStatus().getOrdinal())
136 {
137 case Authenticator.Result.Status.Constants.NO_ATTEMPT:
138 return BaseLoginFilter.LOGIN_NOATTEMPT;
139
140 case Authenticator.Result.Status.Constants.FAILED:
141 setFailureHeader(response, result.getMessage());
142 return BaseLoginFilter.LOGIN_FAILED;
143
144 case Authenticator.Result.Status.Constants.ERROR:
145 setFailureHeader(response, result.getMessage());
146 return BaseLoginFilter.LOGIN_ERROR;
147
148 case Authenticator.Result.Status.Constants.SUCCESS:
149 request.getSession().setAttribute(DefaultAuthenticator.LOGGED_IN_KEY, result.getUser());
150 request.getSession().setAttribute(DefaultAuthenticator.LOGGED_OUT_KEY, null);
151 return BaseLoginFilter.LOGIN_SUCCESS;
152
153
154 default:
155 throw new IllegalStateException("Unknown result: " + result.getStatus().getOrdinal());
156
157 }
158 }
159
160 protected String getPathInfo(HttpServletRequest request)
161 {
162 String context = request.getContextPath();
163 String uri = request.getRequestURI();
164 if (context != null && context.length() > 0)
165 {
166 return uri.substring(context.length());
167 }
168 else
169 {
170 return uri;
171 }
172 }
173
174 private void setFailureHeader(HttpServletResponse response, String message)
175 {
176 response.setHeader(TrustedApplicationUtils.Header.Response.STATUS, Status.ERROR);
177 response.addHeader(TrustedApplicationUtils.Header.Response.ERROR, message);
178 if (log.isInfoEnabled())
179 {
180 log.info(message, new RuntimeException(message));
181 }
182 }
183
184 public void init(FilterConfig config)
185 {
186 this.filterConfig = config;
187 }
188
189 public void destroy()
190 {
191 filterConfig = null;
192 }
193
194
195 public FilterConfig getFilterConfig()
196 {
197 return filterConfig;
198 }
199
200
201 public void setFilterConfig(FilterConfig filterConfig)
202 {
203 if (filterConfig != null)
204 {
205 init(filterConfig);
206 }
207 }
208
209
210
211
212 interface CertificateServer
213 {
214 void writeCertificate(Writer writer) throws IOException;
215 }
216
217 static class CertificateServerImpl implements CertificateServer
218 {
219 final TrustedApplicationsManager appManager;
220
221 CertificateServerImpl(TrustedApplicationsManager appManager)
222 {
223 this.appManager = appManager;
224 }
225
226 public void writeCertificate(Writer writer) throws IOException
227 {
228 CurrentApplication currentApplication = appManager.getCurrentApplication();
229 PublicKey publicKey = currentApplication.getPublicKey();
230
231 try
232 {
233 writer.write(currentApplication.getID());
234 writer.write("\n");
235
236 byte[] key = publicKey.getEncoded();
237 writer.write(new String(Base64.encode(key), TrustedApplicationUtils.Constant.CHARSET_NAME));
238 writer.write("\n");
239 writer.write(TrustedApplicationUtils.Constant.VERSION.toString());
240 writer.write("\n");
241 writer.write(TrustedApplicationUtils.Constant.MAGIC);
242 writer.flush();
243 }
244 catch (UnsupportedEncodingException ex)
245 {
246 throw new AssertionError(ex);
247 }
248 catch (IOException e)
249 {
250 throw new RuntimeException(e);
251 }
252 }
253 }
254
255
256
257
258 interface Authenticator
259 {
260 Result authenticate(HttpServletRequest request);
261
262 static class Result
263 {
264 private final Status status;
265 private final TransportErrorMessage message;
266 private final Principal user;
267
268 Result(Status status)
269 {
270 this(status, null, null);
271 }
272
273 Result(Status status, TransportErrorMessage message)
274 {
275 this(status, message, null);
276
277 notNull("message", message);
278 }
279
280 Result(Status status, Principal principal)
281 {
282 this(status, null, principal);
283
284 notNull("principal", principal);
285 }
286
287 Result(Status status, TransportErrorMessage message, Principal user)
288 {
289 if (status == null)
290 {
291 throw new IllegalArgumentException("status");
292 }
293 this.status = status;
294 this.message = message;
295 this.user = user;
296 }
297
298 public Status getStatus()
299 {
300 return status;
301 }
302
303 public String getMessage()
304 {
305 return message.toString();
306 }
307
308 public Principal getUser()
309 {
310 return user;
311 }
312
313 static final class Status
314 {
315
316
317
318
319 static final class Constants
320 {
321 static final int SUCCESS = 0;
322 static final int FAILED = 1;
323 static final int ERROR = 2;
324 static final int NO_ATTEMPT = 3;
325 }
326
327 static final Status SUCCESS = new Status(Constants.SUCCESS, "success");
328 static final Status FAILED = new Status(Constants.FAILED, "failed");
329 static final Status ERROR = new Status(Constants.ERROR, "error");
330 static final Status NO_ATTEMPT = new Status(Constants.NO_ATTEMPT, "no attempt");
331
332 private final int ordinal;
333 private final String name;
334
335 private Status(int ordinal, String name)
336 {
337 this.ordinal = ordinal;
338 this.name = name;
339 }
340
341 int getOrdinal()
342 {
343 return ordinal;
344 }
345
346 public String toString()
347 {
348 return name;
349 }
350 }
351
352 static class NoAttempt extends Result
353 {
354 NoAttempt()
355 {
356 super(Status.NO_ATTEMPT);
357 }
358 }
359
360 static class Error extends Result
361 {
362 Error(TransportErrorMessage message)
363 {
364 super(Status.ERROR, message);
365 }
366 }
367
368 static class Failure extends Result
369 {
370 Failure(TransportErrorMessage message)
371 {
372 super(Status.FAILED, message);
373 }
374 }
375
376 static class Success extends Result
377 {
378 public Success(Principal principal)
379 {
380 super(Status.SUCCESS, principal);
381 }
382 }
383 }
384 }
385
386 static class AuthenticatorImpl implements Authenticator
387 {
388 final TrustedApplicationsManager appManager;
389 final UserResolver resolver;
390 final RoleMapper roleMapper;
391
392 AuthenticatorImpl(TrustedApplicationsManager appManager, UserResolver resolver, RoleMapper roleMapper)
393 {
394 this.appManager = appManager;
395 this.resolver = resolver;
396 this.roleMapper = roleMapper;
397 }
398
399 public Result authenticate(HttpServletRequest request)
400 {
401 final String certStr = request.getHeader(TrustedApplicationUtils.Header.Request.CERTIFICATE);
402 if (isBlank(certStr))
403 {
404 return new Result.NoAttempt();
405 }
406
407 final String id = request.getHeader(TrustedApplicationUtils.Header.Request.ID);
408 if (isBlank(id))
409 {
410 return new Result.Error(new TransportErrorMessage.ApplicationIdNotFoundInRequest());
411 }
412
413 final String key = request.getHeader(TrustedApplicationUtils.Header.Request.SECRET_KEY);
414 if (isBlank(key))
415 {
416 return new Result.Error(new TransportErrorMessage.SecretKeyNotFoundInRequest());
417 }
418
419 final String magicNumber = request.getHeader(TrustedApplicationUtils.Header.Request.MAGIC);
420
421 final String version = request.getHeader(TrustedApplicationUtils.Header.Request.VERSION);
422 final Integer protocolVersion;
423 try
424 {
425 protocolVersion = (!isBlank(version)) ? new Integer(version) : null;
426 }
427 catch (NumberFormatException e)
428 {
429 return new Result.Error(new TransportErrorMessage.BadProtocolVersion(version));
430 }
431
432
433 if (protocolVersion != null)
434 {
435 if (isBlank(magicNumber))
436 {
437 return new Result.Error(new TransportErrorMessage.MagicNumberNotFoundInRequest());
438 }
439 }
440
441 TrustedApplication app = appManager.getTrustedApplication(id);
442 if (app == null)
443 {
444 return new Result.Failure(new TransportErrorMessage.ApplicationUnknown(id));
445 }
446
447 final ApplicationCertificate certificate;
448 try
449 {
450 certificate = app.decode(new DefaultEncryptedCertificate(id, key, certStr, protocolVersion, magicNumber), request);
451 }
452 catch (InvalidCertificateException ex)
453 {
454 log.warn("Failed to login trusted application: " + app.getID() + " due to: " + ex);
455
456 log.debug("Failed to login trusted application cause", ex);
457 return new Result.Error(ex.getTransportErrorMessage());
458 }
459 final Principal user = resolver.resolve(certificate);
460 if (user == null)
461 {
462 log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' is not found.");
463 return new Result.Failure(new TransportErrorMessage.UserUnknown(certificate.getUserName()));
464 }
465 else if (!roleMapper.canLogin(user, request))
466 {
467
468 log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' cannot login.");
469 return new Result.Failure(new TransportErrorMessage.PermissionDenied());
470 }
471
472 return new Result.Success(user);
473 }
474 }
475
476 private static boolean isBlank(String input)
477 {
478 return (input == null) || input.trim().length() == 0;
479 }
480
481 private static void notNull(String name, Object notNull)
482 {
483 if (notNull == null)
484 {
485 throw new IllegalArgumentException(name + " should not be null");
486 }
487 }
488 }