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