1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.security;
20
21 import java.io.IOException;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.concurrent.CopyOnWriteArrayList;
30 import java.util.concurrent.CopyOnWriteArraySet;
31
32 import org.eclipse.jetty.http.HttpSchemes;
33 import org.eclipse.jetty.http.PathMap;
34 import org.eclipse.jetty.server.AbstractHttpConnection;
35 import org.eclipse.jetty.server.Connector;
36 import org.eclipse.jetty.server.Request;
37 import org.eclipse.jetty.server.Response;
38 import org.eclipse.jetty.server.UserIdentity;
39 import org.eclipse.jetty.util.StringMap;
40 import org.eclipse.jetty.util.TypeUtil;
41 import org.eclipse.jetty.util.security.Constraint;
42
43
44
45
46
47
48
49
50 public class ConstraintSecurityHandler extends SecurityHandler implements ConstraintAware
51 {
52 private final List<ConstraintMapping> _constraintMappings= new CopyOnWriteArrayList<ConstraintMapping>();
53 private final Set<String> _roles = new CopyOnWriteArraySet<String>();
54 private final PathMap _constraintMap = new PathMap();
55 private boolean _strict = true;
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 processConstraintMapping(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 processConstraintMapping(mapping);
232 }
233 }
234 super.doStart();
235 }
236
237 @Override
238 protected void doStop() throws Exception
239 {
240 _constraintMap.clear();
241 _constraintMappings.clear();
242 _roles.clear();
243 super.doStop();
244 }
245
246 protected void processConstraintMapping(ConstraintMapping mapping)
247 {
248 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.get(mapping.getPathSpec());
249 if (mappings == null)
250 {
251 mappings = new StringMap();
252 _constraintMap.put(mapping.getPathSpec(),mappings);
253 }
254 RoleInfo allMethodsRoleInfo = mappings.get(null);
255 if (allMethodsRoleInfo != null && allMethodsRoleInfo.isForbidden())
256 return;
257
258 String httpMethod = mapping.getMethod();
259 RoleInfo roleInfo = mappings.get(httpMethod);
260 if (roleInfo == null)
261 {
262 roleInfo = new RoleInfo();
263 mappings.put(httpMethod,roleInfo);
264 if (allMethodsRoleInfo != null)
265 {
266 roleInfo.combine(allMethodsRoleInfo);
267 }
268 }
269 if (roleInfo.isForbidden())
270 return;
271
272 Constraint constraint = mapping.getConstraint();
273 boolean forbidden = constraint.isForbidden();
274 roleInfo.setForbidden(forbidden);
275 if (forbidden)
276 {
277 if (httpMethod == null)
278 {
279 mappings.clear();
280 mappings.put(null,roleInfo);
281 }
282 }
283 else
284 {
285 UserDataConstraint userDataConstraint = UserDataConstraint.get(constraint.getDataConstraint());
286 roleInfo.setUserDataConstraint(userDataConstraint);
287
288 boolean checked = constraint.getAuthenticate();
289 roleInfo.setChecked(checked);
290 if (roleInfo.isChecked())
291 {
292 if (constraint.isAnyRole())
293 {
294 if (_strict)
295 {
296
297 for (String role : _roles)
298 roleInfo.addRole(role);
299 }
300 else
301
302 roleInfo.setAnyRole(true);
303 }
304 else
305 {
306 String[] newRoles = constraint.getRoles();
307 for (String role : newRoles)
308 {
309 if (_strict &&!_roles.contains(role))
310 throw new IllegalArgumentException("Attempt to use undeclared role: " + role + ", known roles: " + _roles);
311 roleInfo.addRole(role);
312 }
313 }
314 }
315 if (httpMethod == null)
316 {
317 for (Map.Entry<String, RoleInfo> entry : mappings.entrySet())
318 {
319 if (entry.getKey() != null)
320 {
321 RoleInfo specific = entry.getValue();
322 specific.combine(roleInfo);
323 }
324 }
325 }
326 }
327 }
328
329 protected Object prepareConstraintInfo(String pathInContext, Request request)
330 {
331 Map<String, RoleInfo> mappings = (Map<String, RoleInfo>)_constraintMap.match(pathInContext);
332
333 if (mappings != null)
334 {
335 String httpMethod = request.getMethod();
336 RoleInfo roleInfo = mappings.get(httpMethod);
337 if (roleInfo == null)
338 roleInfo = mappings.get(null);
339 return roleInfo;
340 }
341
342 return null;
343 }
344
345 protected boolean checkUserDataPermissions(String pathInContext, Request request, Response response, Object constraintInfo) throws IOException
346 {
347 if (constraintInfo == null)
348 return true;
349
350 RoleInfo roleInfo = (RoleInfo)constraintInfo;
351 if (roleInfo.isForbidden())
352 return false;
353
354
355 UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
356 if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
357 {
358 return true;
359 }
360 AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
361 Connector connector = connection.getConnector();
362
363 if (dataConstraint == UserDataConstraint.Integral)
364 {
365 if (connector.isIntegral(request))
366 return true;
367 if (connector.getIntegralPort() > 0)
368 {
369 String scheme=connector.getIntegralScheme();
370 int port=connector.getIntegralPort();
371 String url = (HttpSchemes.HTTPS.equalsIgnoreCase(scheme) && port==443)
372 ? "https://"+request.getServerName()+request.getRequestURI()
373 : scheme + "://" + request.getServerName() + ":" + port + request.getRequestURI();
374 if (request.getQueryString() != null)
375 url += "?" + request.getQueryString();
376 response.setContentLength(0);
377 response.sendRedirect(url);
378 }
379 else
380 response.sendError(Response.SC_FORBIDDEN,"!Integral");
381
382 request.setHandled(true);
383 return false;
384 }
385 else if (dataConstraint == UserDataConstraint.Confidential)
386 {
387 if (connector.isConfidential(request))
388 return true;
389
390 if (connector.getConfidentialPort() > 0)
391 {
392 String scheme=connector.getConfidentialScheme();
393 int port=connector.getConfidentialPort();
394 String url = (HttpSchemes.HTTPS.equalsIgnoreCase(scheme) && port==443)
395 ? "https://"+request.getServerName()+request.getRequestURI()
396 : scheme + "://" + request.getServerName() + ":" + port + request.getRequestURI();
397 if (request.getQueryString() != null)
398 url += "?" + request.getQueryString();
399 response.setContentLength(0);
400 response.sendRedirect(url);
401 }
402 else
403 response.sendError(Response.SC_FORBIDDEN,"!Confidential");
404
405 request.setHandled(true);
406 return false;
407 }
408 else
409 {
410 throw new IllegalArgumentException("Invalid dataConstraint value: " + dataConstraint);
411 }
412
413 }
414
415 protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
416 {
417 if (constraintInfo == null)
418 {
419 return false;
420 }
421 return ((RoleInfo)constraintInfo).isChecked();
422 }
423
424 @Override
425 protected boolean checkWebResourcePermissions(String pathInContext, Request request, Response response, Object constraintInfo, UserIdentity userIdentity)
426 throws IOException
427 {
428 if (constraintInfo == null)
429 {
430 return true;
431 }
432 RoleInfo roleInfo = (RoleInfo)constraintInfo;
433
434 if (!roleInfo.isChecked())
435 {
436 return true;
437 }
438
439 if (roleInfo.isAnyRole() && request.getAuthType()!=null)
440 return true;
441
442 for (String role : roleInfo.getRoles())
443 {
444 if (userIdentity.isUserInRole(role, null))
445 return true;
446 }
447 return false;
448 }
449
450
451 @Override
452 public void dump(Appendable out,String indent) throws IOException
453 {
454 dumpThis(out);
455 dump(out,indent,
456 Collections.singleton(getLoginService()),
457 Collections.singleton(getIdentityService()),
458 Collections.singleton(getAuthenticator()),
459 Collections.singleton(_roles),
460 _constraintMap.entrySet(),
461 getBeans(),
462 TypeUtil.asList(getHandlers()));
463 }
464 }