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.DefaultEncryptedCertificate;
11 import com.atlassian.security.auth.trustedapps.InvalidCertificateException;
12 import com.atlassian.security.auth.trustedapps.filter.RequestSignatureTool.UnableToVerifySignatureException;
13 import com.atlassian.security.auth.trustedapps.TransportErrorMessage;
14 import com.atlassian.security.auth.trustedapps.TrustedApplication;
15 import com.atlassian.security.auth.trustedapps.TrustedApplicationUtils;
16 import com.atlassian.security.auth.trustedapps.TrustedApplicationsManager;
17 import com.atlassian.security.auth.trustedapps.UserResolver;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 public class TrustedApplicationFilterAuthenticator implements Authenticator
23 {
24 private static final Logger log = LoggerFactory.getLogger(TrustedApplicationFilterAuthenticator.class);
25
26 final TrustedApplicationsManager appManager;
27 final UserResolver resolver;
28 final AuthenticationController authenticationController;
29
30 public TrustedApplicationFilterAuthenticator(TrustedApplicationsManager appManager, UserResolver resolver, AuthenticationController authenticationController)
31 {
32 this.appManager = appManager;
33 this.resolver = resolver;
34 this.authenticationController = authenticationController;
35 }
36
37 private static boolean atLeast(Integer protocolVersion, int required)
38 {
39 return protocolVersion != null && protocolVersion.intValue() >= required;
40 }
41
42 public Result authenticate(HttpServletRequest request, HttpServletResponse response)
43 {
44 final String certStr = request.getHeader(TrustedApplicationUtils.Header.Request.CERTIFICATE);
45 if (isBlank(certStr))
46 {
47 return new Result.NoAttempt();
48 }
49
50 final String id = request.getHeader(TrustedApplicationUtils.Header.Request.ID);
51 if (isBlank(id))
52 {
53 final Result.Error result = new Result.Error(new TransportErrorMessage.ApplicationIdNotFoundInRequest());
54 setFailureHeader(response, result.getMessage());
55 return result;
56 }
57
58 final String key = request.getHeader(TrustedApplicationUtils.Header.Request.SECRET_KEY);
59 if (isBlank(key))
60 {
61 final Result.Error result = new Result.Error(new TransportErrorMessage.SecretKeyNotFoundInRequest());
62 setFailureHeader(response, result.getMessage());
63 return result;
64 }
65
66 final String magicNumber = request.getHeader(TrustedApplicationUtils.Header.Request.MAGIC);
67
68 final String version = request.getHeader(TrustedApplicationUtils.Header.Request.VERSION);
69 final Integer protocolVersion;
70 try
71 {
72 protocolVersion = (!isBlank(version)) ? Integer.parseInt(version) : null;
73 }
74 catch (NumberFormatException e)
75 {
76 final Result.Error result = new Result.Error(new TransportErrorMessage.BadProtocolVersion(version));
77 setFailureHeader(response, result.getMessage());
78 return result;
79 }
80
81 if (atLeast(protocolVersion, 1))
82 {
83 if (isBlank(magicNumber))
84 {
85 final Result.Error result = new Result.Error(new TransportErrorMessage.MagicNumberNotFoundInRequest());
86 setFailureHeader(response, result.getMessage());
87 return result;
88 }
89 }
90
91 TrustedApplication app = appManager.getTrustedApplication(id);
92 if (app == null)
93 {
94 final Result.Failure result = new Result.Failure(new TransportErrorMessage.ApplicationUnknown(id));
95 setFailureHeader(response, result.getMessage());
96 return result;
97 }
98
99 final ApplicationCertificate certificate;
100 try
101 {
102 certificate = app.decode(new DefaultEncryptedCertificate(id, key, certStr, protocolVersion, magicNumber), request);
103 }
104 catch (InvalidCertificateException ex)
105 {
106 log.warn("Failed to login trusted application: " + app.getID() + " due to: " + ex);
107
108 log.debug("Failed to login trusted application cause", ex);
109 final Result.Error result = new Result.Error(ex.getTransportErrorMessage());
110 setFailureHeader(response, result.getMessage());
111 return result;
112 }
113
114 String signedRequestUrl;
115
116 final String signature = request.getHeader(TrustedApplicationUtils.Header.Request.SIGNATURE);
117
118 if (atLeast(protocolVersion, 2) && signature == null)
119 {
120 final Result.Error result = new Result.Error(new TransportErrorMessage.BadSignature());
121 setFailureHeader(response, result.getMessage());
122 return result;
123 }
124
125 if (signature != null)
126 {
127 String expectedSignedUrl;
128
129 StringBuffer sb = request.getRequestURL();
130 String q = request.getQueryString();
131 if (q != null)
132 {
133 sb.append('?');
134 sb.append(q);
135 }
136
137 expectedSignedUrl = sb.toString();
138
139 try
140 {
141 PublicKey publicKey = app.getPublicKey();
142 if (new RequestSignatureTool().verify(certificate.getCreationTime().getTime(), expectedSignedUrl, publicKey, signature))
143 {
144 signedRequestUrl = expectedSignedUrl;
145 }
146 else
147 {
148 log.warn("Failed to login trusted application: " + app.getID() + " due to bad URL signature.");
149
150 final Result.Error result = new Result.Error(new TransportErrorMessage.BadSignature(expectedSignedUrl));
151 setFailureHeader(response, result.getMessage());
152 return result;
153 }
154 }
155 catch (UnableToVerifySignatureException e)
156 {
157 log.warn("Failed to login trusted application: " + app.getID() + " due to: " + e);
158
159 final Result.Error result = new Result.Error(new TransportErrorMessage.BadSignature(expectedSignedUrl));
160 setFailureHeader(response, result.getMessage());
161 return result;
162 }
163 }
164 else
165 {
166 signedRequestUrl = null;
167 }
168
169
170
171 final Principal user = resolver.resolve(certificate);
172 if (user == null)
173 {
174 log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' is not found.");
175 final Result.Failure result = new Result.Failure(new TransportErrorMessage.UserUnknown(certificate.getUserName()));
176 setFailureHeader(response, result.getMessage());
177 return result;
178 }
179 else if (!authenticationController.canLogin(user, request))
180 {
181
182 log.warn("User '" + certificate.getUserName() + "' referenced by trusted application: '" + app.getID() + "' cannot login.");
183 final Result.Failure result = new Result.Failure(new TransportErrorMessage.PermissionDenied());
184 setFailureHeader(response, result.getMessage());
185 return result;
186 }
187
188 if (signedRequestUrl != null)
189 {
190 return new Result.Success(user, signedRequestUrl);
191 }
192 else
193 {
194 return new Result.Success(user);
195 }
196 }
197
198 private static void setFailureHeader(HttpServletResponse response, String message)
199 {
200 response.setHeader(TrustedApplicationUtils.Header.Response.STATUS, TrustedApplicationsFilter.Status.ERROR);
201 response.addHeader(TrustedApplicationUtils.Header.Response.ERROR, message);
202 if (log.isDebugEnabled())
203 {
204 log.debug(message, new RuntimeException(message));
205 }
206 }
207
208 private static boolean isBlank(String input)
209 {
210 return (input == null) || input.trim().length() == 0;
211 }
212 }