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