1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.security;
15
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.Collections;
21 import java.util.HashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.concurrent.CopyOnWriteArrayList;
26 import java.util.concurrent.CopyOnWriteArraySet;
27
28 import org.eclipse.jetty.http.PathMap;
29 import org.eclipse.jetty.http.security.Constraint;
30 import org.eclipse.jetty.server.Connector;
31 import org.eclipse.jetty.server.HttpConnection;
32 import org.eclipse.jetty.server.Request;
33 import org.eclipse.jetty.server.Response;
34 import org.eclipse.jetty.server.SessionManager;
35 import org.eclipse.jetty.server.UserIdentity;
36 import org.eclipse.jetty.server.handler.ContextHandler;
37 import org.eclipse.jetty.server.session.SessionHandler;
38 import org.eclipse.jetty.util.StringMap;
39 import org.eclipse.jetty.util.TypeUtil;
40
41
42
43
44
45
46
47
48 public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
49 {
50 private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<ConstraintMapping>();
51 private final Set<String> _roles = new CopyOnWriteArraySet<String>();
52 private final PathMap _constraintMap = new PathMap();
53 private boolean _strict = true;
54 private SessionHandler _sessionHandler;
55
56
57
58
59
60
61 public boolean isStrict()
62 {
63 return _strict;
64 }
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public void setStrict(boolean strict)
83 {
84 _strict = strict;
85 }
86
87
88
89
90
91 public List<ConstraintMapping> getConstraintMappings()
92 {
93 return _constraintMappings;
94 }
95
96
97 public Set<String> getRoles()
98 {
99 return _roles;
100 }
101
102
103
104
105
106
107
108
109
110
111 public void setConstraintMappings(List<ConstraintMapping> constraintMappings)
112 {
113 setConstraintMappings(constraintMappings,null);
114 }
115
116
117
118
119
120
121
122
123
124 public void setConstraintMappings( ConstraintMapping[] constraintMappings )
125 {
126 setConstraintMappings( Arrays.asList(constraintMappings), null);
127 }
128
129
130
131
132
133
134
135
136
137
138 public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
139 {
140 if (isStarted())
141 throw new IllegalStateException("Started");
142 _constraintMappings.clear();
143 _constraintMappings.addAll(constraintMappings);
144
145 if (roles==null)
146 {
147 roles = new HashSet<String>();
148 for (ConstraintMapping cm : constraintMappings)
149 {
150 String[] cmr = cm.getConstraint().getRoles();
151 if (cmr!=null)
152 {
153 for (String r : cmr)
154 if (!"*".equals(r))
155 roles.add(r);
156 }
157 }
158 }
159 setRoles(roles);
160 }
161
162
163
164
165
166
167
168
169
170 public void setRoles(Set<String> roles)
171 {
172 if (isStarted())
173 throw new IllegalStateException("Started");
174
175 _roles.clear();
176 _roles.addAll(roles);
177 }
178
179
180
181
182
183
184
185 public void addConstraintMapping(ConstraintMapping mapping)
186 {
187 _constraintMappings.add(mapping);
188 if (mapping.getConstraint()!=null && mapping.getConstraint().getRoles()!=null)
189 for (String role : mapping.getConstraint().getRoles())
190 addRole(role);
191
192 if (isStarted())
193 {
194 processContraintMapping(mapping);
195 }
196 }
197
198
199
200
201
202 public void addRole(String role)
203 {
204 boolean modified = _roles.add(role);
205 if (isStarted() && modified && _strict)
206 {
207
208 for (Map<String,RoleInfo> map : (Collection<Map<String,RoleInfo>>)_constraintMap.values())
209 {
210 for (RoleInfo info : map.values())
211 {
212 if (info.isAnyRole())
213 info.addRole(role);
214 }
215 }
216 }
217 }
218
219
220
221
222
223 @Override
224 protected void doStart() throws Exception
225 {
226 _constraintMap.clear();
227 if (_constraintMappings!=null)
228 {
229 for (ConstraintMapping mapping : _constraintMappings)
230 {
231 processContraintMapping(mapping);
232 }
233 }
234
235 if (ContextHandler.getCurrentContext()!=null)
236 _sessionHandler = ContextHandler.getCurrentContext().getContextHandler().getNestedHandlerByClass(SessionHandler.class);
237
238 super.doStart();
239 }
240
241 protected void processContraintMapping(ConstraintMapping mapping)
242 {
243 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
244 if (mappings == null)
245 {
246 mappings = new StringMap();
247 _constraintMap.put(mapping.getPathSpec(),mappings);
248 }
249 RoleInfo allMethodsRoleInfo = mappings.get(null);
250 if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
251 return;
252
253 String httpMethod = mapping.getMethod();
254 RoleInfo roleInfo = mappings.get(httpMethod);
255 if (roleInfo == null)
256 {
257 roleInfo = new RoleInfo();
258 mappings.put(httpMethod,roleInfo);
259 if (allMethodsRoleInfo != null)
260 {
261 roleInfo.combine(allMethodsRoleInfo);
262 }
263 }
264 if (roleInfo.isForbidden())
265 return;
266
267 Constraint constraint = mapping.getConstraint();
268 boolean forbidden = constraint.isForbidden();
269 roleInfo.setForbidden(forbidden);
270 if (forbidden)
271 {
272 if (httpMethod == null)
273 {
274 mappings.clear();
275 mappings.put(null,roleInfo);
276 }
277 }
278 else
279 {
280 UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
281 roleInfo.setUserDataConstraint(userDataConstraint);
282
283 boolean checked = constraint.getAuthenticate();
284 roleInfo.setChecked(checked);
285 if (roleInfo.isChecked())
286 {
287 if (constraint.isAnyRole())
288 {
289 if (_strict)
290 {
291
292 for (String role : _roles)
293 roleInfo.addRole(role);
294 }
295 else
296
297 roleInfo.setAnyRole(true);
298 }
299 else
300 {
301 String[] newRoles = constraint.getRoles();
302 for (String role : newRoles)
303 {
304 if (_strict &&!_roles.contains(role))
305 throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
306 roleInfo.addRole(role);
307 }
308 }
309 }
310 if (httpMethod == null)
311 {
312 for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
313 {
314 if (entry.getKey() != null)
315 {
316 RoleInfo specific = entry.getValue();
317 specific.combine(roleInfo);
318 }
319 }
320 }
321 }
322 }
323
324 protected Object prepareConstraintInfo(String pathInContext, Request request)
325 {
326 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
327
328 if (mappings != null)
329 {
330 String httpMethod = request.getMethod();
331 RoleInfo roleInfo = mappings.get(httpMethod);
332 if (roleInfo == null)
333 roleInfo = mappings.get(null);
334 return roleInfo;
335 }
336
337 return null;
338 }
339
340 protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
341 {
342 if (constraintInfo == null)
343 return true;
344
345 RoleInfo roleInfo = (RoleInfo)constraintInfo;
346 if (roleInfo.isForbidden())
347 return false;
348
349
350 UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
351 if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
352 {
353 return true;
354 }
355 HttpConnection connection = HttpConnection.getCurrentConnection();
356 Connector connector = connection.getConnector();
357
358 if (dataConstraint == UserDataConstraint.Integral)
359 {
360 if (connector.isIntegral(request))
361 return true;
362 if (connector.getConfidentialPort() > 0)
363 {
364 String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI();
365 if (request.getQueryString() != null)
366 url += "?" + request.getQueryString();
367 response.setContentLength(0);
368 response.sendRedirect(url);
369 }
370 else
371 response.sendError(Response.SC_FORBIDDEN,"!Integral");
372
373 request.setHandled(true);
374 return false;
375 }
376 else if (dataConstraint == UserDataConstraint.Confidential)
377 {
378 if (connector.isConfidential(request))
379 return true;
380
381 if (connector.getConfidentialPort() > 0)
382 {
383 String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort()
384 + request.getRequestURI();
385 if (request.getQueryString() != null)
386 url += "?" + request.getQueryString();
387
388 response.setContentLength(0);
389 response.sendRedirect(url);
390 }
391 else
392 response.sendError(Response.SC_FORBIDDEN,"!Confidential");
393
394 request.setHandled(true);
395 return false;
396 }
397 else
398 {
399 throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint);
400 }
401
402 }
403
404 protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
405 {
406 if (constraintInfo == null)
407 {
408 return false;
409 }
410 return ((RoleInfo)constraintInfo).isChecked();
411 }
412
413 @Override
414 protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
415 throws IOException
416 {
417 if (constraintInfo == null)
418 {
419 return true;
420 }
421 RoleInfo roleInfo = (RoleInfo)constraintInfo;
422
423 if (!roleInfo.isChecked())
424 {
425 return true;
426 }
427
428 if (roleInfo.isAnyRole() && request.getAuthType()!=null)
429 return true;
430
431 for (String role : roleInfo.getRoles())
432 {
433 if (userIdentity.isUserInRole(role, null))
434 return true;
435 }
436 return false;
437 }
438
439
440 @Override
441 public void dump(Appendable out,String indent) throws IOException
442 {
443 dumpThis(out);
444 dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),Collections.singleton(_roles),_constraintMap.entrySet());
445 }
446 }