1   package com.atlassian.security.auth.trustedapps.filter;
2   
3   import com.atlassian.security.auth.trustedapps.ApplicationCertificate;
4   import com.atlassian.security.auth.trustedapps.DefaultEncryptedCertificate;
5   import com.atlassian.security.auth.trustedapps.InvalidCertificateException;
6   import com.atlassian.security.auth.trustedapps.TransportErrorMessage;
7   import com.atlassian.security.auth.trustedapps.TrustedApplication;
8   import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
9   import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
10  import com.atlassian.security.auth.trustedapps.UserResolver;
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import javax.servlet.http.HttpServletRequest;
15  import javax.servlet.http.HttpServletResponse;
16  import java.security.Principal;
17  
18  /**
19   *
20   */
21  public class TrustedApplicationFilterAuthenticator implements Authenticator
22  {
23      private static final Logger log = LoggerFactory.getLogger(TrustedApplicationFilterAuthenticator.class);
24  
25      final TrustedApplicationsManager appManager;
26      final UserResolver resolver;
27      final AuthenticationController authenticationController;
28  
29      public TrustedApplicationFilterAuthenticator(TrustedApplicationsManager appManager, UserResolver resolver, AuthenticationController authenticationController)
30      {
31          this.appManager = appManager;
32          this.resolver = resolver;
33          this.authenticationController = authenticationController;
34      }
35  
36      public Result authenticate(HttpServletRequest request, HttpServletResponse response)
37      {
38          final String certStr = request.getHeader(TrustedApplicationUtils.Header.Request.CERTIFICATE);
39          if (isBlank(certStr))
40          {
41              return new Result.NoAttempt();
42          }
43  
44          final String id = request.getHeader(TrustedApplicationUtils.Header.Request.ID);
45          if (isBlank(id))
46          {
47              final Result.Error result = new Result.Error(new TransportErrorMessage.ApplicationIdNotFoundInRequest());
48              setFailureHeader(response, result.getMessage());
49              return result;
50          }
51  
52          final String key = request.getHeader(TrustedApplicationUtils.Header.Request.SECRET_KEY);
53          if (isBlank(key))
54          {
55              final Result.Error result = new Result.Error(new TransportErrorMessage.SecretKeyNotFoundInRequest());
56              setFailureHeader(response, result.getMessage());
57              return result;
58          }
59  
60          final String magicNumber = request.getHeader(TrustedApplicationUtils.Header.Request.MAGIC);
61          // magic number validation is only done from protocol version 2, version 1 had no version header
62          final String version = request.getHeader(TrustedApplicationUtils.Header.Request.VERSION);
63          final Integer protocolVersion;
64          try
65          {
66              protocolVersion = (!isBlank(version)) ? new Integer(version) : null;
67          }
68          catch (NumberFormatException e)
69          {
70              final Result.Error result = new Result.Error(new TransportErrorMessage.BadProtocolVersion(version));
71              setFailureHeader(response, result.getMessage());
72              return result;
73          }
74  
75          // note, this code only handles non-null version - doesn't differentiate between 1 & 2
76          if (protocolVersion != null)
77          {
78              if (isBlank(magicNumber))
79              {
80                  final Result.Error result = new Result.Error(new TransportErrorMessage.MagicNumberNotFoundInRequest());
81                  setFailureHeader(response, result.getMessage());
82                  return result;
83              }
84          }
85  
86          TrustedApplication app = appManager.getTrustedApplication(id);
87          if (app == null)
88          {
89              final Result.Failure result = new Result.Failure(new TransportErrorMessage.ApplicationUnknown(id));
90              setFailureHeader(response, result.getMessage());
91              return result;
92          }
93  
94          final ApplicationCertificate certificate;
95          try
96          {
97              certificate = app.decode(new DefaultEncryptedCertificate(id, key, certStr, protocolVersion, magicNumber), request);
98          }
99          catch (InvalidCertificateException ex)
100         {
101             log.warn("Failed to login trusted application: " + app.getID() + " due to: " + ex);
102             // debug for stacktrace, no need for isDebugEnabled check as there is no string concatenation
103             log.debug("Failed to login trusted application cause", ex);
104             final Result.Error result = new Result.Error(ex.getTransportErrorMessage());
105             setFailureHeader(response, result.getMessage());
106             return result;
107         }
108         final Principal user = resolver.resolve(certificate);
109         if (user == null)
110         {
111             log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' is not found.");
112             final Result.Failure result = new Result.Failure(new TransportErrorMessage.UserUnknown(certificate.getUserName()));
113             setFailureHeader(response, result.getMessage());
114             return result;
115         }
116         else if (!authenticationController.canLogin(user, request))
117         {
118             // user exists but is not allowed to login
119             log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' cannot login.");
120             final Result.Failure result = new Result.Failure(new TransportErrorMessage.PermissionDenied());
121             setFailureHeader(response, result.getMessage());
122             return result;
123         }
124 
125         return new Result.Success(user);
126     }
127 
128     private static void setFailureHeader(HttpServletResponse response, String message)
129     {
130         response.setHeader(TrustedApplicationUtils.Header.Response.STATUS, TrustedApplicationsFilter.Status.ERROR);
131         response.addHeader(TrustedApplicationUtils.Header.Response.ERROR, message);
132         if (log.isInfoEnabled())
133         {
134             log.info(message, new RuntimeException(message));
135         }
136     }
137 
138     private static boolean isBlank(String input)
139     {
140         return (input == null) || input.trim().length() == 0;
141     }
142 }