Lines 1-887
Link Here
|
1 |
/* |
1 |
/* |
2 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
2 |
* Licensed to the Apache Software Foundation (ASF) under one or more |
3 |
* contributor license agreements. See the NOTICE file distributed with |
3 |
* contributor license agreements. See the NOTICE file distributed with |
4 |
* this work for additional information regarding copyright ownership. |
4 |
* this work for additional information regarding copyright ownership. |
5 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
5 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
6 |
* (the "License"); you may not use this file except in compliance with |
6 |
* (the "License"); you may not use this file except in compliance with |
7 |
* the License. You may obtain a copy of the License at |
7 |
* the License. You may obtain a copy of the License at |
8 |
* |
8 |
* |
9 |
* http://www.apache.org/licenses/LICENSE-2.0 |
9 |
* http://www.apache.org/licenses/LICENSE-2.0 |
10 |
* |
10 |
* |
11 |
* Unless required by applicable law or agreed to in writing, software |
11 |
* Unless required by applicable law or agreed to in writing, software |
12 |
* distributed under the License is distributed on an "AS IS" BASIS, |
12 |
* distributed under the License is distributed on an "AS IS" BASIS, |
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 |
* See the License for the specific language governing permissions and |
14 |
* See the License for the specific language governing permissions and |
15 |
* limitations under the License. |
15 |
* limitations under the License. |
16 |
*/ |
16 |
*/ |
17 |
|
17 |
|
18 |
|
18 |
|
19 |
package org.apache.catalina.authenticator; |
19 |
package org.apache.catalina.authenticator; |
20 |
|
20 |
|
21 |
|
21 |
|
22 |
import java.io.IOException; |
22 |
import java.io.IOException; |
23 |
import java.security.MessageDigest; |
23 |
import java.security.MessageDigest; |
24 |
import java.security.NoSuchAlgorithmException; |
24 |
import java.security.NoSuchAlgorithmException; |
25 |
import java.security.Principal; |
25 |
import java.security.Principal; |
26 |
import java.text.SimpleDateFormat; |
26 |
import java.security.cert.X509Certificate; |
27 |
import java.util.Date; |
27 |
import java.text.SimpleDateFormat; |
28 |
import java.util.Locale; |
28 |
import java.util.Date; |
29 |
import java.util.Random; |
29 |
import java.util.Locale; |
30 |
|
30 |
import java.util.Random; |
31 |
import javax.servlet.ServletException; |
31 |
|
32 |
import javax.servlet.http.Cookie; |
32 |
import javax.servlet.ServletException; |
33 |
|
33 |
import javax.servlet.http.Cookie; |
34 |
import org.apache.catalina.Authenticator; |
34 |
|
35 |
import org.apache.catalina.Container; |
35 |
import org.apache.catalina.Authenticator; |
36 |
import org.apache.catalina.Context; |
36 |
import org.apache.catalina.Container; |
37 |
import org.apache.catalina.Lifecycle; |
37 |
import org.apache.catalina.Context; |
38 |
import org.apache.catalina.LifecycleException; |
38 |
import org.apache.catalina.Globals; |
39 |
import org.apache.catalina.LifecycleListener; |
39 |
import org.apache.catalina.Lifecycle; |
40 |
import org.apache.catalina.Pipeline; |
40 |
import org.apache.catalina.LifecycleException; |
41 |
import org.apache.catalina.Realm; |
41 |
import org.apache.catalina.LifecycleListener; |
42 |
import org.apache.catalina.Session; |
42 |
import org.apache.catalina.Pipeline; |
43 |
import org.apache.catalina.Valve; |
43 |
import org.apache.catalina.Realm; |
44 |
import org.apache.catalina.connector.Request; |
44 |
import org.apache.catalina.Session; |
45 |
import org.apache.catalina.connector.Response; |
45 |
import org.apache.catalina.Valve; |
46 |
import org.apache.catalina.deploy.LoginConfig; |
46 |
import org.apache.catalina.connector.Request; |
47 |
import org.apache.catalina.deploy.SecurityConstraint; |
47 |
import org.apache.catalina.connector.Response; |
48 |
import org.apache.catalina.util.DateTool; |
48 |
import org.apache.catalina.deploy.LoginConfig; |
49 |
import org.apache.catalina.util.LifecycleSupport; |
49 |
import org.apache.catalina.deploy.SecurityConstraint; |
50 |
import org.apache.catalina.util.StringManager; |
50 |
import org.apache.catalina.util.DateTool; |
51 |
import org.apache.catalina.valves.ValveBase; |
51 |
import org.apache.catalina.util.LifecycleSupport; |
52 |
import org.apache.commons.logging.Log; |
52 |
import org.apache.catalina.util.StringManager; |
53 |
import org.apache.commons.logging.LogFactory; |
53 |
import org.apache.catalina.valves.ValveBase; |
54 |
|
54 |
import org.apache.commons.logging.Log; |
55 |
|
55 |
import org.apache.commons.logging.LogFactory; |
56 |
/** |
56 |
|
57 |
* Basic implementation of the <b>Valve</b> interface that enforces the |
57 |
|
58 |
* <code><security-constraint></code> elements in the web application |
58 |
/** |
59 |
* deployment descriptor. This functionality is implemented as a Valve |
59 |
* Basic implementation of the <b>Valve</b> interface that enforces the |
60 |
* so that it can be ommitted in environments that do not require these |
60 |
* <code><security-constraint></code> elements in the web application |
61 |
* features. Individual implementations of each supported authentication |
61 |
* deployment descriptor. This functionality is implemented as a Valve |
62 |
* method can subclass this base class as required. |
62 |
* so that it can be ommitted in environments that do not require these |
63 |
* <p> |
63 |
* features. Individual implementations of each supported authentication |
64 |
* <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to |
64 |
* method can subclass this base class as required. |
65 |
* which it is attached (or a parent Container in a hierarchy) must have an |
65 |
* <p> |
66 |
* associated Realm that can be used for authenticating users and enumerating |
66 |
* <b>USAGE CONSTRAINT</b>: When this class is utilized, the Context to |
67 |
* the roles to which they have been assigned. |
67 |
* which it is attached (or a parent Container in a hierarchy) must have an |
68 |
* <p> |
68 |
* associated Realm that can be used for authenticating users and enumerating |
69 |
* <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP |
69 |
* the roles to which they have been assigned. |
70 |
* requests. Requests of any other type will simply be passed through. |
70 |
* <p> |
71 |
* |
71 |
* <b>USAGE CONSTRAINT</b>: This Valve is only useful when processing HTTP |
72 |
* @author Craig R. McClanahan |
72 |
* requests. Requests of any other type will simply be passed through. |
73 |
* @version $Revision: 496025 $ $Date: 2007-01-13 20:18:06 -0700 (Sat, 13 Jan 2007) $ |
73 |
* |
74 |
*/ |
74 |
* @author Craig R. McClanahan |
75 |
|
75 |
* @version $Revision: 496025 $ $Date: 2007-01-14 04:18:06 +0100 (Sun, 14 Jan 2007) $ |
76 |
|
76 |
*/ |
77 |
public abstract class AuthenticatorBase |
77 |
|
78 |
extends ValveBase |
78 |
|
79 |
implements Authenticator, Lifecycle { |
79 |
public abstract class AuthenticatorBase |
80 |
private static Log log = LogFactory.getLog(AuthenticatorBase.class); |
80 |
extends ValveBase |
81 |
|
81 |
implements Authenticator, Lifecycle { |
82 |
|
82 |
private static Log log = LogFactory.getLog(AuthenticatorBase.class); |
83 |
// ----------------------------------------------------- Instance Variables |
83 |
|
84 |
|
84 |
|
85 |
|
85 |
// ----------------------------------------------------- Instance Variables |
86 |
/** |
86 |
|
87 |
* The default message digest algorithm to use if we cannot use |
87 |
|
88 |
* the requested one. |
88 |
/** |
89 |
*/ |
89 |
* The default message digest algorithm to use if we cannot use |
90 |
protected static final String DEFAULT_ALGORITHM = "MD5"; |
90 |
* the requested one. |
91 |
|
91 |
*/ |
92 |
|
92 |
protected static final String DEFAULT_ALGORITHM = "MD5"; |
93 |
/** |
93 |
|
94 |
* The number of random bytes to include when generating a |
94 |
|
95 |
* session identifier. |
95 |
/** |
96 |
*/ |
96 |
* The number of random bytes to include when generating a |
97 |
protected static final int SESSION_ID_BYTES = 16; |
97 |
* session identifier. |
98 |
|
98 |
*/ |
99 |
|
99 |
protected static final int SESSION_ID_BYTES = 16; |
100 |
/** |
100 |
|
101 |
* The message digest algorithm to be used when generating session |
101 |
|
102 |
* identifiers. This must be an algorithm supported by the |
102 |
/** |
103 |
* <code>java.security.MessageDigest</code> class on your platform. |
103 |
* The message digest algorithm to be used when generating session |
104 |
*/ |
104 |
* identifiers. This must be an algorithm supported by the |
105 |
protected String algorithm = DEFAULT_ALGORITHM; |
105 |
* <code>java.security.MessageDigest</code> class on your platform. |
106 |
|
106 |
*/ |
107 |
|
107 |
protected String algorithm = DEFAULT_ALGORITHM; |
108 |
/** |
108 |
|
109 |
* Should we cache authenticated Principals if the request is part of |
109 |
|
110 |
* an HTTP session? |
110 |
/** |
111 |
*/ |
111 |
* Should we cache authenticated Principals if the request is part of |
112 |
protected boolean cache = true; |
112 |
* an HTTP session? |
113 |
|
113 |
*/ |
114 |
|
114 |
protected boolean cache = true; |
115 |
/** |
115 |
|
116 |
* The Context to which this Valve is attached. |
116 |
|
117 |
*/ |
117 |
/** |
118 |
protected Context context = null; |
118 |
* The Context to which this Valve is attached. |
119 |
|
119 |
*/ |
120 |
|
120 |
protected Context context = null; |
121 |
/** |
121 |
|
122 |
* Return the MessageDigest implementation to be used when |
122 |
|
123 |
* creating session identifiers. |
123 |
/** |
124 |
*/ |
124 |
* Return the MessageDigest implementation to be used when |
125 |
protected MessageDigest digest = null; |
125 |
* creating session identifiers. |
126 |
|
126 |
*/ |
127 |
|
127 |
protected MessageDigest digest = null; |
128 |
/** |
128 |
|
129 |
* A String initialization parameter used to increase the entropy of |
129 |
|
130 |
* the initialization of our random number generator. |
130 |
/** |
131 |
*/ |
131 |
* A String initialization parameter used to increase the entropy of |
132 |
protected String entropy = null; |
132 |
* the initialization of our random number generator. |
133 |
|
133 |
*/ |
134 |
|
134 |
protected String entropy = null; |
135 |
/** |
135 |
|
136 |
* Descriptive information about this implementation. |
136 |
|
137 |
*/ |
137 |
/** |
138 |
protected static final String info = |
138 |
* Descriptive information about this implementation. |
139 |
"org.apache.catalina.authenticator.AuthenticatorBase/1.0"; |
139 |
*/ |
140 |
|
140 |
protected static final String info = |
141 |
/** |
141 |
"org.apache.catalina.authenticator.AuthenticatorBase/1.0"; |
142 |
* Flag to determine if we disable proxy caching, or leave the issue |
142 |
|
143 |
* up to the webapp developer. |
143 |
/** |
144 |
*/ |
144 |
* Flag to determine if we disable proxy caching, or leave the issue |
145 |
protected boolean disableProxyCaching = true; |
145 |
* up to the webapp developer. |
146 |
|
146 |
*/ |
147 |
/** |
147 |
protected boolean disableProxyCaching = true; |
148 |
* Flag to determine if we disable proxy caching with headers incompatible |
148 |
|
149 |
* with IE |
149 |
/** |
150 |
*/ |
150 |
* Flag to determine if we disable proxy caching with headers incompatible |
151 |
protected boolean securePagesWithPragma = true; |
151 |
* with IE |
152 |
|
152 |
*/ |
153 |
/** |
153 |
protected boolean securePagesWithPragma = true; |
154 |
* The lifecycle event support for this component. |
154 |
|
155 |
*/ |
155 |
/** |
156 |
protected LifecycleSupport lifecycle = new LifecycleSupport(this); |
156 |
* The lifecycle event support for this component. |
157 |
|
157 |
*/ |
158 |
|
158 |
protected LifecycleSupport lifecycle = new LifecycleSupport(this); |
159 |
/** |
159 |
|
160 |
* A random number generator to use when generating session identifiers. |
160 |
|
161 |
*/ |
161 |
/** |
162 |
protected Random random = null; |
162 |
* A random number generator to use when generating session identifiers. |
163 |
|
163 |
*/ |
164 |
|
164 |
protected Random random = null; |
165 |
/** |
165 |
|
166 |
* The Java class name of the random number generator class to be used |
166 |
|
167 |
* when generating session identifiers. |
167 |
/** |
168 |
*/ |
168 |
* The Java class name of the random number generator class to be used |
169 |
protected String randomClass = "java.security.SecureRandom"; |
169 |
* when generating session identifiers. |
170 |
|
170 |
*/ |
171 |
|
171 |
protected String randomClass = "java.security.SecureRandom"; |
172 |
/** |
172 |
|
173 |
* The string manager for this package. |
173 |
|
174 |
*/ |
174 |
/** |
175 |
protected static final StringManager sm = |
175 |
* The string manager for this package. |
176 |
StringManager.getManager(Constants.Package); |
176 |
*/ |
177 |
|
177 |
protected static final StringManager sm = |
178 |
|
178 |
StringManager.getManager(Constants.Package); |
179 |
/** |
179 |
|
180 |
* The SingleSignOn implementation in our request processing chain, |
180 |
|
181 |
* if there is one. |
181 |
/** |
182 |
*/ |
182 |
* The SingleSignOn implementation in our request processing chain, |
183 |
protected SingleSignOn sso = null; |
183 |
* if there is one. |
184 |
|
184 |
*/ |
185 |
|
185 |
protected SingleSignOn sso = null; |
186 |
/** |
186 |
|
187 |
* Has this component been started? |
187 |
|
188 |
*/ |
188 |
/** |
189 |
protected boolean started = false; |
189 |
* Has this component been started? |
190 |
|
190 |
*/ |
191 |
|
191 |
protected boolean started = false; |
192 |
/** |
192 |
|
193 |
* "Expires" header always set to Date(1), so generate once only |
193 |
|
194 |
*/ |
194 |
/** |
195 |
private static final String DATE_ONE = |
195 |
* "Expires" header always set to Date(1), so generate once only |
196 |
(new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER, |
196 |
*/ |
197 |
Locale.US)).format(new Date(1)); |
197 |
private static final String DATE_ONE = |
198 |
|
198 |
(new SimpleDateFormat(DateTool.HTTP_RESPONSE_DATE_HEADER, |
199 |
|
199 |
Locale.US)).format(new Date(1)); |
200 |
// ------------------------------------------------------------- Properties |
200 |
|
201 |
|
201 |
|
202 |
|
202 |
// ------------------------------------------------------------- Properties |
203 |
/** |
203 |
|
204 |
* Return the message digest algorithm for this Manager. |
204 |
|
205 |
*/ |
205 |
/** |
206 |
public String getAlgorithm() { |
206 |
* Return the message digest algorithm for this Manager. |
207 |
|
207 |
*/ |
208 |
return (this.algorithm); |
208 |
public String getAlgorithm() { |
209 |
|
209 |
|
210 |
} |
210 |
return (this.algorithm); |
211 |
|
211 |
|
212 |
|
212 |
} |
213 |
/** |
213 |
|
214 |
* Set the message digest algorithm for this Manager. |
214 |
|
215 |
* |
215 |
/** |
216 |
* @param algorithm The new message digest algorithm |
216 |
* Set the message digest algorithm for this Manager. |
217 |
*/ |
217 |
* |
218 |
public void setAlgorithm(String algorithm) { |
218 |
* @param algorithm The new message digest algorithm |
219 |
|
219 |
*/ |
220 |
this.algorithm = algorithm; |
220 |
public void setAlgorithm(String algorithm) { |
221 |
|
221 |
|
222 |
} |
222 |
this.algorithm = algorithm; |
223 |
|
223 |
|
224 |
|
224 |
} |
225 |
/** |
225 |
|
226 |
* Return the cache authenticated Principals flag. |
226 |
|
227 |
*/ |
227 |
/** |
228 |
public boolean getCache() { |
228 |
* Return the cache authenticated Principals flag. |
229 |
|
229 |
*/ |
230 |
return (this.cache); |
230 |
public boolean getCache() { |
231 |
|
231 |
|
232 |
} |
232 |
return (this.cache); |
233 |
|
233 |
|
234 |
|
234 |
} |
235 |
/** |
235 |
|
236 |
* Set the cache authenticated Principals flag. |
236 |
|
237 |
* |
237 |
/** |
238 |
* @param cache The new cache flag |
238 |
* Set the cache authenticated Principals flag. |
239 |
*/ |
239 |
* |
240 |
public void setCache(boolean cache) { |
240 |
* @param cache The new cache flag |
241 |
|
241 |
*/ |
242 |
this.cache = cache; |
242 |
public void setCache(boolean cache) { |
243 |
|
243 |
|
244 |
} |
244 |
this.cache = cache; |
245 |
|
245 |
|
246 |
|
246 |
} |
247 |
/** |
247 |
|
248 |
* Return the Container to which this Valve is attached. |
248 |
|
249 |
*/ |
249 |
/** |
250 |
public Container getContainer() { |
250 |
* Return the Container to which this Valve is attached. |
251 |
|
251 |
*/ |
252 |
return (this.context); |
252 |
public Container getContainer() { |
253 |
|
253 |
|
254 |
} |
254 |
return (this.context); |
255 |
|
255 |
|
256 |
|
256 |
} |
257 |
/** |
257 |
|
258 |
* Set the Container to which this Valve is attached. |
258 |
|
259 |
* |
259 |
/** |
260 |
* @param container The container to which we are attached |
260 |
* Set the Container to which this Valve is attached. |
261 |
*/ |
261 |
* |
262 |
public void setContainer(Container container) { |
262 |
* @param container The container to which we are attached |
263 |
|
263 |
*/ |
264 |
if (!(container instanceof Context)) |
264 |
public void setContainer(Container container) { |
265 |
throw new IllegalArgumentException |
265 |
|
266 |
(sm.getString("authenticator.notContext")); |
266 |
if (!(container instanceof Context)) |
267 |
|
267 |
throw new IllegalArgumentException |
268 |
super.setContainer(container); |
268 |
(sm.getString("authenticator.notContext")); |
269 |
this.context = (Context) container; |
269 |
|
270 |
|
270 |
super.setContainer(container); |
271 |
} |
271 |
this.context = (Context) container; |
272 |
|
272 |
|
273 |
|
273 |
} |
274 |
/** |
274 |
|
275 |
* Return the entropy increaser value, or compute a semi-useful value |
275 |
|
276 |
* if this String has not yet been set. |
276 |
/** |
277 |
*/ |
277 |
* Return the entropy increaser value, or compute a semi-useful value |
278 |
public String getEntropy() { |
278 |
* if this String has not yet been set. |
279 |
|
279 |
*/ |
280 |
// Calculate a semi-useful value if this has not been set |
280 |
public String getEntropy() { |
281 |
if (this.entropy == null) |
281 |
|
282 |
setEntropy(this.toString()); |
282 |
// Calculate a semi-useful value if this has not been set |
283 |
|
283 |
if (this.entropy == null) |
284 |
return (this.entropy); |
284 |
setEntropy(this.toString()); |
285 |
|
285 |
|
286 |
} |
286 |
return (this.entropy); |
287 |
|
287 |
|
288 |
|
288 |
} |
289 |
/** |
289 |
|
290 |
* Set the entropy increaser value. |
290 |
|
291 |
* |
291 |
/** |
292 |
* @param entropy The new entropy increaser value |
292 |
* Set the entropy increaser value. |
293 |
*/ |
293 |
* |
294 |
public void setEntropy(String entropy) { |
294 |
* @param entropy The new entropy increaser value |
295 |
|
295 |
*/ |
296 |
this.entropy = entropy; |
296 |
public void setEntropy(String entropy) { |
297 |
|
297 |
|
298 |
} |
298 |
this.entropy = entropy; |
299 |
|
299 |
|
300 |
|
300 |
} |
301 |
/** |
301 |
|
302 |
* Return descriptive information about this Valve implementation. |
302 |
|
303 |
*/ |
303 |
/** |
304 |
public String getInfo() { |
304 |
* Return descriptive information about this Valve implementation. |
305 |
|
305 |
*/ |
306 |
return (info); |
306 |
public String getInfo() { |
307 |
|
307 |
|
308 |
} |
308 |
return (info); |
309 |
|
309 |
|
310 |
|
310 |
} |
311 |
/** |
311 |
|
312 |
* Return the random number generator class name. |
312 |
|
313 |
*/ |
313 |
/** |
314 |
public String getRandomClass() { |
314 |
* Return the random number generator class name. |
315 |
|
315 |
*/ |
316 |
return (this.randomClass); |
316 |
public String getRandomClass() { |
317 |
|
317 |
|
318 |
} |
318 |
return (this.randomClass); |
319 |
|
319 |
|
320 |
|
320 |
} |
321 |
/** |
321 |
|
322 |
* Set the random number generator class name. |
322 |
|
323 |
* |
323 |
/** |
324 |
* @param randomClass The new random number generator class name |
324 |
* Set the random number generator class name. |
325 |
*/ |
325 |
* |
326 |
public void setRandomClass(String randomClass) { |
326 |
* @param randomClass The new random number generator class name |
327 |
|
327 |
*/ |
328 |
this.randomClass = randomClass; |
328 |
public void setRandomClass(String randomClass) { |
329 |
|
329 |
|
330 |
} |
330 |
this.randomClass = randomClass; |
331 |
|
331 |
|
332 |
/** |
332 |
} |
333 |
* Return the flag that states if we add headers to disable caching by |
333 |
|
334 |
* proxies. |
334 |
/** |
335 |
*/ |
335 |
* Return the flag that states if we add headers to disable caching by |
336 |
public boolean getDisableProxyCaching() { |
336 |
* proxies. |
337 |
return disableProxyCaching; |
337 |
*/ |
338 |
} |
338 |
public boolean getDisableProxyCaching() { |
339 |
|
339 |
return disableProxyCaching; |
340 |
/** |
340 |
} |
341 |
* Set the value of the flag that states if we add headers to disable |
341 |
|
342 |
* caching by proxies. |
342 |
/** |
343 |
* @param nocache <code>true</code> if we add headers to disable proxy |
343 |
* Set the value of the flag that states if we add headers to disable |
344 |
* caching, <code>false</code> if we leave the headers alone. |
344 |
* caching by proxies. |
345 |
*/ |
345 |
* @param nocache <code>true</code> if we add headers to disable proxy |
346 |
public void setDisableProxyCaching(boolean nocache) { |
346 |
* caching, <code>false</code> if we leave the headers alone. |
347 |
disableProxyCaching = nocache; |
347 |
*/ |
348 |
} |
348 |
public void setDisableProxyCaching(boolean nocache) { |
349 |
|
349 |
disableProxyCaching = nocache; |
350 |
/** |
350 |
} |
351 |
* Return the flag that states, if proxy caching is disabled, what headers |
351 |
|
352 |
* we add to disable the caching. |
352 |
/** |
353 |
*/ |
353 |
* Return the flag that states, if proxy caching is disabled, what headers |
354 |
public boolean getSecurePagesWithPragma() { |
354 |
* we add to disable the caching. |
355 |
return securePagesWithPragma; |
355 |
*/ |
356 |
} |
356 |
public boolean getSecurePagesWithPragma() { |
357 |
|
357 |
return securePagesWithPragma; |
358 |
/** |
358 |
} |
359 |
* Set the value of the flag that states what headers we add to disable |
359 |
|
360 |
* proxy caching. |
360 |
/** |
361 |
* @param securePagesWithPragma <code>true</code> if we add headers which |
361 |
* Set the value of the flag that states what headers we add to disable |
362 |
* are incompatible with downloading office documents in IE under SSL but |
362 |
* proxy caching. |
363 |
* which fix a caching problem in Mozilla. |
363 |
* @param securePagesWithPragma <code>true</code> if we add headers which |
364 |
*/ |
364 |
* are incompatible with downloading office documents in IE under SSL but |
365 |
public void setSecurePagesWithPragma(boolean securePagesWithPragma) { |
365 |
* which fix a caching problem in Mozilla. |
366 |
this.securePagesWithPragma = securePagesWithPragma; |
366 |
*/ |
367 |
} |
367 |
public void setSecurePagesWithPragma(boolean securePagesWithPragma) { |
368 |
|
368 |
this.securePagesWithPragma = securePagesWithPragma; |
369 |
// --------------------------------------------------------- Public Methods |
369 |
} |
370 |
|
370 |
|
371 |
|
371 |
// --------------------------------------------------------- Public Methods |
372 |
/** |
372 |
|
373 |
* Enforce the security restrictions in the web application deployment |
373 |
|
374 |
* descriptor of our associated Context. |
374 |
/** |
375 |
* |
375 |
* Enforce the security restrictions in the web application deployment |
376 |
* @param request Request to be processed |
376 |
* descriptor of our associated Context. |
377 |
* @param response Response to be processed |
377 |
* |
378 |
* |
378 |
* @param request Request to be processed |
379 |
* @exception IOException if an input/output error occurs |
379 |
* @param response Response to be processed |
380 |
* @exception ServletException if thrown by a processing element |
380 |
* |
381 |
*/ |
381 |
* @exception IOException if an input/output error occurs |
382 |
public void invoke(Request request, Response response) |
382 |
* @exception ServletException if thrown by a processing element |
383 |
throws IOException, ServletException { |
383 |
*/ |
384 |
|
384 |
public void invoke(Request request, Response response) |
385 |
if (log.isDebugEnabled()) |
385 |
throws IOException, ServletException { |
386 |
log.debug("Security checking request " + |
386 |
|
387 |
request.getMethod() + " " + request.getRequestURI()); |
387 |
if (log.isDebugEnabled()) |
388 |
LoginConfig config = this.context.getLoginConfig(); |
388 |
log.debug("Security checking request " + |
389 |
|
389 |
request.getMethod() + " " + request.getRequestURI()); |
390 |
// Have we got a cached authenticated Principal to record? |
390 |
LoginConfig config = this.context.getLoginConfig(); |
391 |
if (cache) { |
391 |
|
392 |
Principal principal = request.getUserPrincipal(); |
392 |
// Have we got a cached authenticated Principal to record? |
393 |
if (principal == null) { |
393 |
if (cache) { |
394 |
Session session = request.getSessionInternal(false); |
394 |
Principal principal = request.getUserPrincipal(); |
395 |
if (session != null) { |
395 |
if (principal == null) { |
396 |
principal = session.getPrincipal(); |
396 |
Session session = request.getSessionInternal(false); |
397 |
if (principal != null) { |
397 |
if (session != null) { |
398 |
if (log.isDebugEnabled()) |
398 |
principal = session.getPrincipal(); |
399 |
log.debug("We have cached auth type " + |
399 |
if (principal != null) { |
400 |
session.getAuthType() + |
400 |
if (log.isDebugEnabled()) |
401 |
" for principal " + |
401 |
log.debug("We have cached auth type " + |
402 |
session.getPrincipal()); |
402 |
session.getAuthType() + |
403 |
request.setAuthType(session.getAuthType()); |
403 |
" for principal " + |
404 |
request.setUserPrincipal(principal); |
404 |
session.getPrincipal()); |
405 |
} |
405 |
request.setAuthType(session.getAuthType()); |
406 |
} |
406 |
request.setUserPrincipal(principal); |
407 |
} |
407 |
} |
408 |
} |
408 |
} |
409 |
|
409 |
} |
410 |
// Special handling for form-based logins to deal with the case |
410 |
} |
411 |
// where the login form (and therefore the "j_security_check" URI |
411 |
|
412 |
// to which it submits) might be outside the secured area |
412 |
// Special handling for form-based logins to deal with the case |
413 |
String contextPath = this.context.getPath(); |
413 |
// where the login form (and therefore the "j_security_check" URI |
414 |
String requestURI = request.getDecodedRequestURI(); |
414 |
// to which it submits) might be outside the secured area |
415 |
if (requestURI.startsWith(contextPath) && |
415 |
String contextPath = this.context.getPath(); |
416 |
requestURI.endsWith(Constants.FORM_ACTION)) { |
416 |
String requestURI = request.getDecodedRequestURI(); |
417 |
if (!authenticate(request, response, config)) { |
417 |
if (requestURI.startsWith(contextPath) && |
418 |
if (log.isDebugEnabled()) |
418 |
requestURI.endsWith(Constants.FORM_ACTION)) { |
419 |
log.debug(" Failed authenticate() test ??" + requestURI ); |
419 |
if (!authenticate(request, response, config)) { |
420 |
return; |
420 |
if (log.isDebugEnabled()) |
421 |
} |
421 |
log.debug(" Failed authenticate() test ??" + requestURI ); |
422 |
} |
422 |
return; |
423 |
|
423 |
} |
424 |
Realm realm = this.context.getRealm(); |
424 |
} |
425 |
// Is this request URI subject to a security constraint? |
425 |
|
426 |
SecurityConstraint [] constraints |
426 |
Realm realm = this.context.getRealm(); |
427 |
= realm.findSecurityConstraints(request, this.context); |
427 |
// Is this request URI subject to a security constraint? |
428 |
|
428 |
SecurityConstraint [] constraints |
429 |
if ((constraints == null) /* && |
429 |
= realm.findSecurityConstraints(request, this.context); |
430 |
(!Constants.FORM_METHOD.equals(config.getAuthMethod())) */ ) { |
430 |
|
431 |
if (log.isDebugEnabled()) |
431 |
// Make sure that constrained resources are not cached by web proxies |
432 |
log.debug(" Not subject to any constraint"); |
432 |
// or browsers as caching can provide a security hole |
433 |
getNext().invoke(request, response); |
433 |
if (disableProxyCaching && |
434 |
return; |
434 |
// FIXME: Disabled for Mozilla FORM support over SSL |
435 |
} |
435 |
// (improper caching issue) |
436 |
|
436 |
//!request.isSecure() && |
437 |
// Make sure that constrained resources are not cached by web proxies |
437 |
!"POST".equalsIgnoreCase(request.getMethod())) { |
438 |
// or browsers as caching can provide a security hole |
438 |
if (securePagesWithPragma) { |
439 |
if (disableProxyCaching && |
439 |
// FIXME: These cause problems with downloading office docs |
440 |
// FIXME: Disabled for Mozilla FORM support over SSL |
440 |
// from IE under SSL and may not be needed for newer Mozilla |
441 |
// (improper caching issue) |
441 |
// clients. |
442 |
//!request.isSecure() && |
442 |
response.setHeader("Pragma", "No-cache"); |
443 |
!"POST".equalsIgnoreCase(request.getMethod())) { |
443 |
response.setHeader("Cache-Control", "no-cache"); |
444 |
if (securePagesWithPragma) { |
444 |
} else { |
445 |
// FIXME: These cause problems with downloading office docs |
445 |
response.setHeader("Cache-Control", "private"); |
446 |
// from IE under SSL and may not be needed for newer Mozilla |
446 |
} |
447 |
// clients. |
447 |
response.setHeader("Expires", DATE_ONE); |
448 |
response.setHeader("Pragma", "No-cache"); |
448 |
} |
449 |
response.setHeader("Cache-Control", "no-cache"); |
449 |
|
450 |
} else { |
450 |
int i; |
451 |
response.setHeader("Cache-Control", "private"); |
451 |
if (constraints != null) { |
452 |
} |
452 |
// Enforce any user data constraint for this security constraint |
453 |
response.setHeader("Expires", DATE_ONE); |
453 |
if (log.isDebugEnabled()) { |
454 |
} |
454 |
log.debug(" Calling hasUserDataPermission()"); |
455 |
|
455 |
} |
456 |
int i; |
456 |
if (!realm.hasUserDataPermission(request, response, |
457 |
// Enforce any user data constraint for this security constraint |
457 |
constraints)) { |
458 |
if (log.isDebugEnabled()) { |
458 |
if (log.isDebugEnabled()) { |
459 |
log.debug(" Calling hasUserDataPermission()"); |
459 |
log.debug(" Failed hasUserDataPermission() test"); |
460 |
} |
460 |
} |
461 |
if (!realm.hasUserDataPermission(request, response, |
461 |
/* |
462 |
constraints)) { |
462 |
* ASSERT: Authenticator already set the appropriate |
463 |
if (log.isDebugEnabled()) { |
463 |
* HTTP status code, so we do not have to do anything special |
464 |
log.debug(" Failed hasUserDataPermission() test"); |
464 |
*/ |
465 |
} |
465 |
return; |
466 |
/* |
466 |
} |
467 |
* ASSERT: Authenticator already set the appropriate |
467 |
} |
468 |
* HTTP status code, so we do not have to do anything special |
468 |
|
469 |
*/ |
469 |
// Since authenticate modifies the response on failure, |
470 |
return; |
470 |
// we have to check for allow-from-all first. |
471 |
} |
471 |
boolean authRequired; |
472 |
|
472 |
if (constraints == null) { |
473 |
// Since authenticate modifies the response on failure, |
473 |
authRequired = false; |
474 |
// we have to check for allow-from-all first. |
474 |
} else { |
475 |
boolean authRequired = true; |
475 |
authRequired = true; |
476 |
for(i=0; i < constraints.length && authRequired; i++) { |
476 |
for(i=0; i < constraints.length && authRequired; i++) { |
477 |
if(!constraints[i].getAuthConstraint()) { |
477 |
if(!constraints[i].getAuthConstraint()) { |
478 |
authRequired = false; |
478 |
authRequired = false; |
479 |
} else if(!constraints[i].getAllRoles()) { |
479 |
} else if(!constraints[i].getAllRoles()) { |
480 |
String [] roles = constraints[i].findAuthRoles(); |
480 |
String [] roles = constraints[i].findAuthRoles(); |
481 |
if(roles == null || roles.length == 0) { |
481 |
if(roles == null || roles.length == 0) { |
482 |
authRequired = false; |
482 |
authRequired = false; |
483 |
} |
483 |
} |
484 |
} |
484 |
} |
485 |
} |
485 |
} |
486 |
|
486 |
} |
487 |
if(authRequired) { |
487 |
|
488 |
if (log.isDebugEnabled()) { |
488 |
if (!authRequired) |
489 |
log.debug(" Calling authenticate()"); |
489 |
{ |
490 |
} |
490 |
authRequired = |
491 |
if (!authenticate(request, response, config)) { |
491 |
request.getCoyoteRequest().getMimeHeaders(). |
492 |
if (log.isDebugEnabled()) { |
492 |
getValue("authorization") != null; |
493 |
log.debug(" Failed authenticate() test"); |
493 |
} |
494 |
} |
494 |
|
495 |
/* |
495 |
if (!authRequired) |
496 |
* ASSERT: Authenticator already set the appropriate |
496 |
{ |
497 |
* HTTP status code, so we do not have to do anything |
497 |
X509Certificate[] certs = |
498 |
* special |
498 |
(X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR); |
499 |
*/ |
499 |
|
500 |
return; |
500 |
authRequired = certs != null && certs.length > 0; |
501 |
} |
501 |
} |
502 |
} |
502 |
|
503 |
|
503 |
if(authRequired) { |
504 |
if (log.isDebugEnabled()) { |
504 |
if (log.isDebugEnabled()) { |
505 |
log.debug(" Calling accessControl()"); |
505 |
log.debug(" Calling authenticate()"); |
506 |
} |
506 |
} |
507 |
if (!realm.hasResourcePermission(request, response, |
507 |
if (!authenticate(request, response, config)) { |
508 |
constraints, |
508 |
if (log.isDebugEnabled()) { |
509 |
this.context)) { |
509 |
log.debug(" Failed authenticate() test"); |
510 |
if (log.isDebugEnabled()) { |
510 |
} |
511 |
log.debug(" Failed accessControl() test"); |
511 |
/* |
512 |
} |
512 |
* ASSERT: Authenticator already set the appropriate |
513 |
/* |
513 |
* HTTP status code, so we do not have to do anything |
514 |
* ASSERT: AccessControl method has already set the |
514 |
* special |
515 |
* appropriate HTTP status code, so we do not have to do |
515 |
*/ |
516 |
* anything special |
516 |
return; |
517 |
*/ |
517 |
} |
518 |
return; |
518 |
} |
519 |
} |
519 |
|
520 |
|
520 |
if (constraints != null) { |
521 |
// Any and all specified constraints have been satisfied |
521 |
if (log.isDebugEnabled()) { |
522 |
if (log.isDebugEnabled()) { |
522 |
log.debug(" Calling accessControl()"); |
523 |
log.debug(" Successfully passed all security constraints"); |
523 |
} |
524 |
} |
524 |
if (!realm.hasResourcePermission(request, response, |
525 |
getNext().invoke(request, response); |
525 |
constraints, |
526 |
|
526 |
this.context)) { |
527 |
} |
527 |
if (log.isDebugEnabled()) { |
528 |
|
528 |
log.debug(" Failed accessControl() test"); |
529 |
|
529 |
} |
530 |
// ------------------------------------------------------ Protected Methods |
530 |
/* |
531 |
|
531 |
* ASSERT: AccessControl method has already set the |
532 |
|
532 |
* appropriate HTTP status code, so we do not have to do |
533 |
|
533 |
* anything special |
534 |
|
534 |
*/ |
535 |
/** |
535 |
return; |
536 |
* Associate the specified single sign on identifier with the |
536 |
} |
537 |
* specified Session. |
537 |
} |
538 |
* |
538 |
|
539 |
* @param ssoId Single sign on identifier |
539 |
// Any and all specified constraints have been satisfied |
540 |
* @param session Session to be associated |
540 |
if (log.isDebugEnabled()) { |
541 |
*/ |
541 |
log.debug(" Successfully passed all security constraints"); |
542 |
protected void associate(String ssoId, Session session) { |
542 |
} |
543 |
|
543 |
getNext().invoke(request, response); |
544 |
if (sso == null) |
544 |
|
545 |
return; |
545 |
} |
546 |
sso.associate(ssoId, session); |
546 |
|
547 |
|
547 |
|
548 |
} |
548 |
// ------------------------------------------------------ Protected Methods |
549 |
|
549 |
|
550 |
|
550 |
|
551 |
/** |
551 |
|
552 |
* Authenticate the user making this request, based on the specified |
552 |
|
553 |
* login configuration. Return <code>true</code> if any specified |
553 |
/** |
554 |
* constraint has been satisfied, or <code>false</code> if we have |
554 |
* Associate the specified single sign on identifier with the |
555 |
* created a response challenge already. |
555 |
* specified Session. |
556 |
* |
556 |
* |
557 |
* @param request Request we are processing |
557 |
* @param ssoId Single sign on identifier |
558 |
* @param response Response we are creating |
558 |
* @param session Session to be associated |
559 |
* @param config Login configuration describing how authentication |
559 |
*/ |
560 |
* should be performed |
560 |
protected void associate(String ssoId, Session session) { |
561 |
* |
561 |
|
562 |
* @exception IOException if an input/output error occurs |
562 |
if (sso == null) |
563 |
*/ |
563 |
return; |
564 |
protected abstract boolean authenticate(Request request, |
564 |
sso.associate(ssoId, session); |
565 |
Response response, |
565 |
|
566 |
LoginConfig config) |
566 |
} |
567 |
throws IOException; |
567 |
|
568 |
|
568 |
|
569 |
|
569 |
/** |
570 |
/** |
570 |
* Authenticate the user making this request, based on the specified |
571 |
* Generate and return a new session identifier for the cookie that |
571 |
* login configuration. Return <code>true</code> if any specified |
572 |
* identifies an SSO principal. |
572 |
* constraint has been satisfied, or <code>false</code> if we have |
573 |
*/ |
573 |
* created a response challenge already. |
574 |
protected synchronized String generateSessionId() { |
574 |
* |
575 |
|
575 |
* @param request Request we are processing |
576 |
// Generate a byte array containing a session identifier |
576 |
* @param response Response we are creating |
577 |
byte bytes[] = new byte[SESSION_ID_BYTES]; |
577 |
* @param config Login configuration describing how authentication |
578 |
getRandom().nextBytes(bytes); |
578 |
* should be performed |
579 |
bytes = getDigest().digest(bytes); |
579 |
* |
580 |
|
580 |
* @exception IOException if an input/output error occurs |
581 |
// Render the result as a String of hexadecimal digits |
581 |
*/ |
582 |
StringBuffer result = new StringBuffer(); |
582 |
protected abstract boolean authenticate(Request request, |
583 |
for (int i = 0; i < bytes.length; i++) { |
583 |
Response response, |
584 |
byte b1 = (byte) ((bytes[i] & 0xf0) >> 4); |
584 |
LoginConfig config) |
585 |
byte b2 = (byte) (bytes[i] & 0x0f); |
585 |
throws IOException; |
586 |
if (b1 < 10) |
586 |
|
587 |
result.append((char) ('0' + b1)); |
587 |
|
588 |
else |
588 |
/** |
589 |
result.append((char) ('A' + (b1 - 10))); |
589 |
* Generate and return a new session identifier for the cookie that |
590 |
if (b2 < 10) |
590 |
* identifies an SSO principal. |
591 |
result.append((char) ('0' + b2)); |
591 |
*/ |
592 |
else |
592 |
protected synchronized String generateSessionId() { |
593 |
result.append((char) ('A' + (b2 - 10))); |
593 |
|
594 |
} |
594 |
// Generate a byte array containing a session identifier |
595 |
return (result.toString()); |
595 |
byte bytes[] = new byte[SESSION_ID_BYTES]; |
596 |
|
596 |
getRandom().nextBytes(bytes); |
597 |
} |
597 |
bytes = getDigest().digest(bytes); |
598 |
|
598 |
|
599 |
|
599 |
// Render the result as a String of hexadecimal digits |
600 |
/** |
600 |
StringBuffer result = new StringBuffer(); |
601 |
* Return the MessageDigest object to be used for calculating |
601 |
for (int i = 0; i < bytes.length; i++) { |
602 |
* session identifiers. If none has been created yet, initialize |
602 |
byte b1 = (byte) ((bytes[i] & 0xf0) >> 4); |
603 |
* one the first time this method is called. |
603 |
byte b2 = (byte) (bytes[i] & 0x0f); |
604 |
*/ |
604 |
if (b1 < 10) |
605 |
protected synchronized MessageDigest getDigest() { |
605 |
result.append((char) ('0' + b1)); |
606 |
|
606 |
else |
607 |
if (this.digest == null) { |
607 |
result.append((char) ('A' + (b1 - 10))); |
608 |
try { |
608 |
if (b2 < 10) |
609 |
this.digest = MessageDigest.getInstance(algorithm); |
609 |
result.append((char) ('0' + b2)); |
610 |
} catch (NoSuchAlgorithmException e) { |
610 |
else |
611 |
try { |
611 |
result.append((char) ('A' + (b2 - 10))); |
612 |
this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); |
612 |
} |
613 |
} catch (NoSuchAlgorithmException f) { |
613 |
return (result.toString()); |
614 |
this.digest = null; |
614 |
|
615 |
} |
615 |
} |
616 |
} |
616 |
|
617 |
} |
617 |
|
618 |
|
618 |
/** |
619 |
return (this.digest); |
619 |
* Return the MessageDigest object to be used for calculating |
620 |
|
620 |
* session identifiers. If none has been created yet, initialize |
621 |
} |
621 |
* one the first time this method is called. |
622 |
|
622 |
*/ |
623 |
|
623 |
protected synchronized MessageDigest getDigest() { |
624 |
/** |
624 |
|
625 |
* Return the random number generator instance we should use for |
625 |
if (this.digest == null) { |
626 |
* generating session identifiers. If there is no such generator |
626 |
try { |
627 |
* currently defined, construct and seed a new one. |
627 |
this.digest = MessageDigest.getInstance(algorithm); |
628 |
*/ |
628 |
} catch (NoSuchAlgorithmException e) { |
629 |
protected synchronized Random getRandom() { |
629 |
try { |
630 |
|
630 |
this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); |
631 |
if (this.random == null) { |
631 |
} catch (NoSuchAlgorithmException f) { |
632 |
try { |
632 |
this.digest = null; |
633 |
Class clazz = Class.forName(randomClass); |
633 |
} |
634 |
this.random = (Random) clazz.newInstance(); |
634 |
} |
635 |
long seed = System.currentTimeMillis(); |
635 |
} |
636 |
char entropy[] = getEntropy().toCharArray(); |
636 |
|
637 |
for (int i = 0; i < entropy.length; i++) { |
637 |
return (this.digest); |
638 |
long update = ((byte) entropy[i]) << ((i % 8) * 8); |
638 |
|
639 |
seed ^= update; |
639 |
} |
640 |
} |
640 |
|
641 |
this.random.setSeed(seed); |
641 |
|
642 |
} catch (Exception e) { |
642 |
/** |
643 |
this.random = new java.util.Random(); |
643 |
* Return the random number generator instance we should use for |
644 |
} |
644 |
* generating session identifiers. If there is no such generator |
645 |
} |
645 |
* currently defined, construct and seed a new one. |
646 |
|
646 |
*/ |
647 |
return (this.random); |
647 |
protected synchronized Random getRandom() { |
648 |
|
648 |
|
649 |
} |
649 |
if (this.random == null) { |
650 |
|
650 |
try { |
651 |
|
651 |
Class clazz = Class.forName(randomClass); |
652 |
/** |
652 |
this.random = (Random) clazz.newInstance(); |
653 |
* Attempts reauthentication to the <code>Realm</code> using |
653 |
long seed = System.currentTimeMillis(); |
654 |
* the credentials included in argument <code>entry</code>. |
654 |
char entropy[] = getEntropy().toCharArray(); |
655 |
* |
655 |
for (int i = 0; i < entropy.length; i++) { |
656 |
* @param ssoId identifier of SingleSignOn session with which the |
656 |
long update = ((byte) entropy[i]) << ((i % 8) * 8); |
657 |
* caller is associated |
657 |
seed ^= update; |
658 |
* @param request the request that needs to be authenticated |
658 |
} |
659 |
*/ |
659 |
this.random.setSeed(seed); |
660 |
protected boolean reauthenticateFromSSO(String ssoId, Request request) { |
660 |
} catch (Exception e) { |
661 |
|
661 |
this.random = new java.util.Random(); |
662 |
if (sso == null || ssoId == null) |
662 |
} |
663 |
return false; |
663 |
} |
664 |
|
664 |
|
665 |
boolean reauthenticated = false; |
665 |
return (this.random); |
666 |
|
666 |
|
667 |
Container parent = getContainer(); |
667 |
} |
668 |
if (parent != null) { |
668 |
|
669 |
Realm realm = parent.getRealm(); |
669 |
|
670 |
if (realm != null) { |
670 |
/** |
671 |
reauthenticated = sso.reauthenticate(ssoId, realm, request); |
671 |
* Attempts reauthentication to the <code>Realm</code> using |
672 |
} |
672 |
* the credentials included in argument <code>entry</code>. |
673 |
} |
673 |
* |
674 |
|
674 |
* @param ssoId identifier of SingleSignOn session with which the |
675 |
if (reauthenticated) { |
675 |
* caller is associated |
676 |
associate(ssoId, request.getSessionInternal(true)); |
676 |
* @param request the request that needs to be authenticated |
677 |
|
677 |
*/ |
678 |
if (log.isDebugEnabled()) { |
678 |
protected boolean reauthenticateFromSSO(String ssoId, Request request) { |
679 |
log.debug(" Reauthenticated cached principal '" + |
679 |
|
680 |
request.getUserPrincipal().getName() + |
680 |
if (sso == null || ssoId == null) |
681 |
"' with auth type '" + request.getAuthType() + "'"); |
681 |
return false; |
682 |
} |
682 |
|
683 |
} |
683 |
boolean reauthenticated = false; |
684 |
|
684 |
|
685 |
return reauthenticated; |
685 |
Container parent = getContainer(); |
686 |
} |
686 |
if (parent != null) { |
687 |
|
687 |
Realm realm = parent.getRealm(); |
688 |
|
688 |
if (realm != null) { |
689 |
/** |
689 |
reauthenticated = sso.reauthenticate(ssoId, realm, request); |
690 |
* Register an authenticated Principal and authentication type in our |
690 |
} |
691 |
* request, in the current session (if there is one), and with our |
691 |
} |
692 |
* SingleSignOn valve, if there is one. Set the appropriate cookie |
692 |
|
693 |
* to be returned. |
693 |
if (reauthenticated) { |
694 |
* |
694 |
associate(ssoId, request.getSessionInternal(true)); |
695 |
* @param request The servlet request we are processing |
695 |
|
696 |
* @param response The servlet response we are generating |
696 |
if (log.isDebugEnabled()) { |
697 |
* @param principal The authenticated Principal to be registered |
697 |
log.debug(" Reauthenticated cached principal '" + |
698 |
* @param authType The authentication type to be registered |
698 |
request.getUserPrincipal().getName() + |
699 |
* @param username Username used to authenticate (if any) |
699 |
"' with auth type '" + request.getAuthType() + "'"); |
700 |
* @param password Password used to authenticate (if any) |
700 |
} |
701 |
*/ |
701 |
} |
702 |
protected void register(Request request, Response response, |
702 |
|
703 |
Principal principal, String authType, |
703 |
return reauthenticated; |
704 |
String username, String password) { |
704 |
} |
705 |
|
705 |
|
706 |
if (log.isDebugEnabled()) { |
706 |
|
707 |
// Bugzilla 39255: http://issues.apache.org/bugzilla/show_bug.cgi?id=39255 |
707 |
/** |
708 |
String name = (principal == null) ? "none" : principal.getName(); |
708 |
* Register an authenticated Principal and authentication type in our |
709 |
log.debug("Authenticated '" + name + "' with type '" |
709 |
* request, in the current session (if there is one), and with our |
710 |
+ authType + "'"); |
710 |
* SingleSignOn valve, if there is one. Set the appropriate cookie |
711 |
} |
711 |
* to be returned. |
712 |
|
712 |
* |
713 |
// Cache the authentication information in our request |
713 |
* @param request The servlet request we are processing |
714 |
request.setAuthType(authType); |
714 |
* @param response The servlet response we are generating |
715 |
request.setUserPrincipal(principal); |
715 |
* @param principal The authenticated Principal to be registered |
716 |
|
716 |
* @param authType The authentication type to be registered |
717 |
Session session = request.getSessionInternal(false); |
717 |
* @param username Username used to authenticate (if any) |
718 |
// Cache the authentication information in our session, if any |
718 |
* @param password Password used to authenticate (if any) |
719 |
if (cache) { |
719 |
*/ |
720 |
if (session != null) { |
720 |
protected void register(Request request, Response response, |
721 |
session.setAuthType(authType); |
721 |
Principal principal, String authType, |
722 |
session.setPrincipal(principal); |
722 |
String username, String password) { |
723 |
if (username != null) |
723 |
|
724 |
session.setNote(Constants.SESS_USERNAME_NOTE, username); |
724 |
if (log.isDebugEnabled()) { |
725 |
else |
725 |
// Bugzilla 39255: http://issues.apache.org/bugzilla/show_bug.cgi?id=39255 |
726 |
session.removeNote(Constants.SESS_USERNAME_NOTE); |
726 |
String name = (principal == null) ? "none" : principal.getName(); |
727 |
if (password != null) |
727 |
log.debug("Authenticated '" + name + "' with type '" |
728 |
session.setNote(Constants.SESS_PASSWORD_NOTE, password); |
728 |
+ authType + "'"); |
729 |
else |
729 |
} |
730 |
session.removeNote(Constants.SESS_PASSWORD_NOTE); |
730 |
|
731 |
} |
731 |
// Cache the authentication information in our request |
732 |
} |
732 |
request.setAuthType(authType); |
733 |
|
733 |
request.setUserPrincipal(principal); |
734 |
// Construct a cookie to be returned to the client |
734 |
|
735 |
if (sso == null) |
735 |
Session session = request.getSessionInternal(false); |
736 |
return; |
736 |
// Cache the authentication information in our session, if any |
737 |
|
737 |
if (cache) { |
738 |
// Only create a new SSO entry if the SSO did not already set a note |
738 |
if (session != null) { |
739 |
// for an existing entry (as it would do with subsequent requests |
739 |
session.setAuthType(authType); |
740 |
// for DIGEST and SSL authenticated contexts) |
740 |
session.setPrincipal(principal); |
741 |
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE); |
741 |
if (username != null) |
742 |
if (ssoId == null) { |
742 |
session.setNote(Constants.SESS_USERNAME_NOTE, username); |
743 |
// Construct a cookie to be returned to the client |
743 |
else |
744 |
ssoId = generateSessionId(); |
744 |
session.removeNote(Constants.SESS_USERNAME_NOTE); |
745 |
Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId); |
745 |
if (password != null) |
746 |
cookie.setMaxAge(-1); |
746 |
session.setNote(Constants.SESS_PASSWORD_NOTE, password); |
747 |
cookie.setPath("/"); |
747 |
else |
748 |
|
748 |
session.removeNote(Constants.SESS_PASSWORD_NOTE); |
749 |
// Bugzilla 41217 |
749 |
} |
750 |
cookie.setSecure(request.isSecure()); |
750 |
} |
751 |
|
751 |
|
752 |
// Bugzilla 34724 |
752 |
// Construct a cookie to be returned to the client |
753 |
String ssoDomain = sso.getCookieDomain(); |
753 |
if (sso == null) |
754 |
if(ssoDomain != null) { |
754 |
return; |
755 |
cookie.setDomain(ssoDomain); |
755 |
|
756 |
} |
756 |
// Only create a new SSO entry if the SSO did not already set a note |
757 |
|
757 |
// for an existing entry (as it would do with subsequent requests |
758 |
response.addCookie(cookie); |
758 |
// for DIGEST and SSL authenticated contexts) |
759 |
|
759 |
String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE); |
760 |
// Register this principal with our SSO valve |
760 |
if (ssoId == null) { |
761 |
sso.register(ssoId, principal, authType, username, password); |
761 |
// Construct a cookie to be returned to the client |
762 |
request.setNote(Constants.REQ_SSOID_NOTE, ssoId); |
762 |
ssoId = generateSessionId(); |
763 |
|
763 |
Cookie cookie = new Cookie(Constants.SINGLE_SIGN_ON_COOKIE, ssoId); |
764 |
} else { |
764 |
cookie.setMaxAge(-1); |
765 |
// Update the SSO session with the latest authentication data |
765 |
cookie.setPath("/"); |
766 |
sso.update(ssoId, principal, authType, username, password); |
766 |
|
767 |
} |
767 |
// Bugzilla 41217 |
768 |
|
768 |
cookie.setSecure(request.isSecure()); |
769 |
// Fix for Bug 10040 |
769 |
|
770 |
// Always associate a session with a new SSO reqistration. |
770 |
// Bugzilla 34724 |
771 |
// SSO entries are only removed from the SSO registry map when |
771 |
String ssoDomain = sso.getCookieDomain(); |
772 |
// associated sessions are destroyed; if a new SSO entry is created |
772 |
if(ssoDomain != null) { |
773 |
// above for this request and the user never revisits the context, the |
773 |
cookie.setDomain(ssoDomain); |
774 |
// SSO entry will never be cleared if we don't associate the session |
774 |
} |
775 |
if (session == null) |
775 |
|
776 |
session = request.getSessionInternal(true); |
776 |
response.addCookie(cookie); |
777 |
sso.associate(ssoId, session); |
777 |
|
778 |
|
778 |
// Register this principal with our SSO valve |
779 |
} |
779 |
sso.register(ssoId, principal, authType, username, password); |
780 |
|
780 |
request.setNote(Constants.REQ_SSOID_NOTE, ssoId); |
781 |
|
781 |
|
782 |
// ------------------------------------------------------ Lifecycle Methods |
782 |
} else { |
783 |
|
783 |
// Update the SSO session with the latest authentication data |
784 |
|
784 |
sso.update(ssoId, principal, authType, username, password); |
785 |
/** |
785 |
} |
786 |
* Add a lifecycle event listener to this component. |
786 |
|
787 |
* |
787 |
// Fix for Bug 10040 |
788 |
* @param listener The listener to add |
788 |
// Always associate a session with a new SSO reqistration. |
789 |
*/ |
789 |
// SSO entries are only removed from the SSO registry map when |
790 |
public void addLifecycleListener(LifecycleListener listener) { |
790 |
// associated sessions are destroyed; if a new SSO entry is created |
791 |
|
791 |
// above for this request and the user never revisits the context, the |
792 |
lifecycle.addLifecycleListener(listener); |
792 |
// SSO entry will never be cleared if we don't associate the session |
793 |
|
793 |
if (session == null) |
794 |
} |
794 |
session = request.getSessionInternal(true); |
795 |
|
795 |
sso.associate(ssoId, session); |
796 |
|
796 |
|
797 |
/** |
797 |
} |
798 |
* Get the lifecycle listeners associated with this lifecycle. If this |
798 |
|
799 |
* Lifecycle has no listeners registered, a zero-length array is returned. |
799 |
|
800 |
*/ |
800 |
// ------------------------------------------------------ Lifecycle Methods |
801 |
public LifecycleListener[] findLifecycleListeners() { |
801 |
|
802 |
|
802 |
|
803 |
return lifecycle.findLifecycleListeners(); |
803 |
/** |
804 |
|
804 |
* Add a lifecycle event listener to this component. |
805 |
} |
805 |
* |
806 |
|
806 |
* @param listener The listener to add |
807 |
|
807 |
*/ |
808 |
/** |
808 |
public void addLifecycleListener(LifecycleListener listener) { |
809 |
* Remove a lifecycle event listener from this component. |
809 |
|
810 |
* |
810 |
lifecycle.addLifecycleListener(listener); |
811 |
* @param listener The listener to remove |
811 |
|
812 |
*/ |
812 |
} |
813 |
public void removeLifecycleListener(LifecycleListener listener) { |
813 |
|
814 |
|
814 |
|
815 |
lifecycle.removeLifecycleListener(listener); |
815 |
/** |
816 |
|
816 |
* Get the lifecycle listeners associated with this lifecycle. If this |
817 |
} |
817 |
* Lifecycle has no listeners registered, a zero-length array is returned. |
818 |
|
818 |
*/ |
819 |
|
819 |
public LifecycleListener[] findLifecycleListeners() { |
820 |
/** |
820 |
|
821 |
* Prepare for the beginning of active use of the public methods of this |
821 |
return lifecycle.findLifecycleListeners(); |
822 |
* component. This method should be called after <code>configure()</code>, |
822 |
|
823 |
* and before any of the public methods of the component are utilized. |
823 |
} |
824 |
* |
824 |
|
825 |
* @exception LifecycleException if this component detects a fatal error |
825 |
|
826 |
* that prevents this component from being used |
826 |
/** |
827 |
*/ |
827 |
* Remove a lifecycle event listener from this component. |
828 |
public void start() throws LifecycleException { |
828 |
* |
829 |
|
829 |
* @param listener The listener to remove |
830 |
// Validate and update our current component state |
830 |
*/ |
831 |
if (started) |
831 |
public void removeLifecycleListener(LifecycleListener listener) { |
832 |
throw new LifecycleException |
832 |
|
833 |
(sm.getString("authenticator.alreadyStarted")); |
833 |
lifecycle.removeLifecycleListener(listener); |
834 |
lifecycle.fireLifecycleEvent(START_EVENT, null); |
834 |
|
835 |
started = true; |
835 |
} |
836 |
|
836 |
|
837 |
// Look up the SingleSignOn implementation in our request processing |
837 |
|
838 |
// path, if there is one |
838 |
/** |
839 |
Container parent = context.getParent(); |
839 |
* Prepare for the beginning of active use of the public methods of this |
840 |
while ((sso == null) && (parent != null)) { |
840 |
* component. This method should be called after <code>configure()</code>, |
841 |
if (!(parent instanceof Pipeline)) { |
841 |
* and before any of the public methods of the component are utilized. |
842 |
parent = parent.getParent(); |
842 |
* |
843 |
continue; |
843 |
* @exception LifecycleException if this component detects a fatal error |
844 |
} |
844 |
* that prevents this component from being used |
845 |
Valve valves[] = ((Pipeline) parent).getValves(); |
845 |
*/ |
846 |
for (int i = 0; i < valves.length; i++) { |
846 |
public void start() throws LifecycleException { |
847 |
if (valves[i] instanceof SingleSignOn) { |
847 |
|
848 |
sso = (SingleSignOn) valves[i]; |
848 |
// Validate and update our current component state |
849 |
break; |
849 |
if (started) |
850 |
} |
850 |
throw new LifecycleException |
851 |
} |
851 |
(sm.getString("authenticator.alreadyStarted")); |
852 |
if (sso == null) |
852 |
lifecycle.fireLifecycleEvent(START_EVENT, null); |
853 |
parent = parent.getParent(); |
853 |
started = true; |
854 |
} |
854 |
|
855 |
if (log.isDebugEnabled()) { |
855 |
// Look up the SingleSignOn implementation in our request processing |
856 |
if (sso != null) |
856 |
// path, if there is one |
857 |
log.debug("Found SingleSignOn Valve at " + sso); |
857 |
Container parent = context.getParent(); |
858 |
else |
858 |
while ((sso == null) && (parent != null)) { |
859 |
log.debug("No SingleSignOn Valve is present"); |
859 |
if (!(parent instanceof Pipeline)) { |
860 |
} |
860 |
parent = parent.getParent(); |
861 |
|
861 |
continue; |
862 |
} |
862 |
} |
863 |
|
863 |
Valve valves[] = ((Pipeline) parent).getValves(); |
864 |
|
864 |
for (int i = 0; i < valves.length; i++) { |
865 |
/** |
865 |
if (valves[i] instanceof SingleSignOn) { |
866 |
* Gracefully terminate the active use of the public methods of this |
866 |
sso = (SingleSignOn) valves[i]; |
867 |
* component. This method should be the last one called on a given |
867 |
break; |
868 |
* instance of this component. |
868 |
} |
869 |
* |
869 |
} |
870 |
* @exception LifecycleException if this component detects a fatal error |
870 |
if (sso == null) |
871 |
* that needs to be reported |
871 |
parent = parent.getParent(); |
872 |
*/ |
872 |
} |
873 |
public void stop() throws LifecycleException { |
873 |
if (log.isDebugEnabled()) { |
874 |
|
874 |
if (sso != null) |
875 |
// Validate and update our current component state |
875 |
log.debug("Found SingleSignOn Valve at " + sso); |
876 |
if (!started) |
876 |
else |
877 |
throw new LifecycleException |
877 |
log.debug("No SingleSignOn Valve is present"); |
878 |
(sm.getString("authenticator.notStarted")); |
878 |
} |
879 |
lifecycle.fireLifecycleEvent(STOP_EVENT, null); |
879 |
|
880 |
started = false; |
880 |
} |
881 |
|
881 |
|
882 |
sso = null; |
882 |
|
883 |
|
883 |
/** |
884 |
} |
884 |
* Gracefully terminate the active use of the public methods of this |
885 |
|
885 |
* component. This method should be the last one called on a given |
886 |
|
886 |
* instance of this component. |
887 |
} |
887 |
* |
|
|
888 |
* @exception LifecycleException if this component detects a fatal error |
889 |
* that needs to be reported |
890 |
*/ |
891 |
public void stop() throws LifecycleException { |
892 |
|
893 |
// Validate and update our current component state |
894 |
if (!started) |
895 |
throw new LifecycleException |
896 |
(sm.getString("authenticator.notStarted")); |
897 |
lifecycle.fireLifecycleEvent(STOP_EVENT, null); |
898 |
started = false; |
899 |
|
900 |
sso = null; |
901 |
|
902 |
} |
903 |
|
904 |
|
905 |
} |