1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.jetspeed.portlet;
18
19 import java.io.IOException;
20 import java.security.AccessControlContext;
21 import java.security.AccessController;
22 import java.util.HashMap;
23 import java.util.StringTokenizer;
24
25 import javax.portlet.ActionRequest;
26 import javax.portlet.ActionResponse;
27 import javax.portlet.PortletConfig;
28 import javax.portlet.PortletContext;
29 import javax.portlet.PortletException;
30 import javax.portlet.PortletMode;
31 import javax.portlet.PortletPreferences;
32 import javax.portlet.RenderRequest;
33 import javax.portlet.RenderResponse;
34 import javax.security.auth.Subject;
35
36 import org.apache.commons.codec.binary.Base64;
37 import org.apache.commons.httpclient.HttpClient;
38 import org.apache.commons.httpclient.HttpMethod;
39 import org.apache.commons.httpclient.NameValuePair;
40 import org.apache.commons.httpclient.UsernamePasswordCredentials;
41 import org.apache.commons.httpclient.auth.AuthScope;
42 import org.apache.commons.httpclient.auth.AuthState;
43 import org.apache.commons.httpclient.auth.BasicScheme;
44 import org.apache.commons.httpclient.methods.PostMethod;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.apache.jetspeed.rewriter.WebContentRewriter;
48 import org.apache.jetspeed.security.JSSubject;
49 import org.apache.jetspeed.sso.SSOContext;
50 import org.apache.jetspeed.sso.SSOException;
51 import org.apache.jetspeed.sso.SSOProvider;
52 import org.apache.portals.messaging.PortletMessaging;
53
54
55 /***
56 * SSOWebContentPortlet
57 *
58 * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
59 * @version $Id: SSOWebContentPortlet.java 516448 2007-03-09 16:25:47Z ate $
60 */
61 public class SSOWebContentPortlet extends WebContentPortlet
62 {
63
64
65
66 public static final String SSO_TYPE = "sso.type";
67
68 public static final String SSO_TYPE_HTTP = "http";
69 public static final String SSO_TYPE_BASIC = "basic";
70 public static final String SSO_TYPE_BASIC_PREEMPTIVE = "basic.preemptive";
71
72 public static final String SSO_TYPE_FORM = "form";
73 public static final String SSO_TYPE_FORM_GET = "form.get";
74 public static final String SSO_TYPE_FORM_POST = "form.post";
75
76 public static final String SSO_TYPE_URL = "url";
77 public static final String SSO_TYPE_URL_BASE64 = "url.base64";
78
79 public static final String SSO_TYPE_CERTIFICATE = "certificate";
80
81 public static final String SSO_TYPE_DEFAULT = SSO_TYPE_BASIC;
82
83
84
85 public static final String BASIC_AUTH_SCHEME_NAME = (new BasicScheme()).getSchemeName();
86
87
88
89
90
91 public static final String SSO_TYPE_URL_USERNAME_PARAM = "sso.url.Principal";
92 public static final String SSO_TYPE_URL_PASSWORD_PARAM = "sso.url.Credential";
93
94
95
96 public static final String SSO_TYPE_FORM_ACTION_URL = "sso.form.Action";
97 public static final String SSO_TYPE_FORM_ACTION_ARGS = "sso.form.Args";
98 public static final String SSO_TYPE_FORM_USERNAME_FIELD = "sso.form.Principal";
99 public static final String SSO_TYPE_FORM_PASSWORD_FIELD = "sso.form.Credential";
100
101
102
103 public static final String SSO_REQUEST_ATTRIBUTE_USERNAME = "sso.ra.username";
104 public static final String SSO_REQUEST_ATTRIBUTE_PASSWORD = "sso.ra.password";
105
106
107
108 public static final String SSO_EDIT_FIELD_PRINCIPAL = "ssoPrincipal";
109 public static final String SSO_EDIT_FIELD_CREDENTIAL = "ssoCredential";
110
111
112
113 public static final String FORM_AUTH_STATE = "ssowebcontent.form.authstate" ;
114
115
116
117
118 protected final static Log log = LogFactory.getLog(SSOWebContentPortlet.class);
119
120
121
122
123 private PortletContext context;
124 private SSOProvider sso;
125
126
127
128
129 public void init(PortletConfig config) throws PortletException
130 {
131 super.init(config);
132 context = getPortletContext();
133 sso = (SSOProvider)context.getAttribute("cps:SSO");
134 if (null == sso)
135 {
136 throw new PortletException("Failed to find SSO Provider on portlet initialization");
137 }
138 }
139
140 public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
141 throws PortletException, IOException
142 {
143
144 String webContentParameter = actionRequest.getParameter(WebContentRewriter.ACTION_PARAMETER_URL);
145 String ssoPrincipal = actionRequest.getParameter(SSO_EDIT_FIELD_PRINCIPAL);
146 String ssoCredential = actionRequest.getParameter(SSO_EDIT_FIELD_CREDENTIAL);
147
148
149 super.processAction(actionRequest, actionResponse);
150
151
152 if (webContentParameter == null || actionRequest.getPortletMode() == PortletMode.EDIT)
153 {
154
155
156 String site = actionRequest.getPreferences().getValue("SRC", "");
157
158 try
159 {
160 Subject subject = getSubject();
161 if (sso.hasSSOCredentials(subject, site))
162 {
163 sso.updateCredentialsForSite(getSubject(), ssoPrincipal, site, ssoCredential);
164 }
165 else
166 {
167 sso.addCredentialsForSite(getSubject(), ssoPrincipal, site, ssoCredential);
168 }
169 }
170 catch (SSOException e)
171 {
172 throw new PortletException(e);
173 }
174 }
175 }
176
177 public void doView(RenderRequest request, RenderResponse response)
178 throws PortletException, IOException
179 {
180 String site = request.getPreferences().getValue("SRC", null);
181
182 if (site == null)
183 {
184
185 request.setAttribute(PARAM_VIEW_PAGE, this.getPortletConfig().getInitParameter(PARAM_EDIT_PAGE));
186 setupPreferencesEdit(request, response);
187 }
188 else try
189 {
190 Subject subject = getSubject();
191 SSOContext context = sso.getCredentials(subject, site);
192 request.setAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME, context.getRemotePrincipalName());
193 request.setAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD, context.getRemoteCredential());
194 }
195 catch (SSOException e)
196 {
197 if (e.getMessage().equals(SSOException.NO_CREDENTIALS_FOR_SITE))
198 {
199
200
201 request.setAttribute(PARAM_VIEW_PAGE, this.getPortletConfig().getInitParameter(PARAM_EDIT_PAGE));
202 setupPreferencesEdit(request, response);
203 }
204 else
205 {
206 throw new PortletException(e);
207 }
208 }
209
210 super.doView(request, response);
211 }
212
213
214 public void doEdit(RenderRequest request, RenderResponse response)
215 throws PortletException, IOException
216 {
217 try
218 {
219 Subject subject = getSubject();
220 String site = request.getPreferences().getValue("SRC", "");
221 SSOContext context = sso.getCredentials(subject, site);
222 getContext(request).put(SSO_EDIT_FIELD_PRINCIPAL, context.getRemotePrincipalName());
223 getContext(request).put(SSO_EDIT_FIELD_CREDENTIAL, context.getRemoteCredential());
224 }
225 catch (SSOException e)
226 {
227 if (e.getMessage().equals(SSOException.NO_CREDENTIALS_FOR_SITE))
228 {
229
230
231 getContext(request).put(SSO_EDIT_FIELD_PRINCIPAL, "");
232 getContext(request).put(SSO_EDIT_FIELD_CREDENTIAL, "");
233 }
234 else
235 {
236 throw new PortletException(e);
237 }
238 }
239
240 super.doEdit(request, response);
241 }
242
243 private Subject getSubject()
244 {
245 AccessControlContext context = AccessController.getContext();
246 return JSSubject.getSubject(context);
247 }
248
249 protected byte[] doPreemptiveAuthentication(HttpClient client,HttpMethod method, RenderRequest request, RenderResponse response)
250 {
251 byte[] result = super.doPreemptiveAuthentication(client, method, request, response);
252 if ( result != null)
253 {
254
255 return result ;
256 }
257
258
259
260 PortletPreferences prefs = request.getPreferences();
261 String type = getSingleSignOnAuthType(prefs);
262
263 if (type.equalsIgnoreCase(SSO_TYPE_BASIC_PREEMPTIVE))
264 {
265
266 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
267 if (userName == null) userName = "";
268 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
269 if (password == null) password = "";
270
271
272 method.setDoAuthentication(true);
273 method.getHostAuthState().setPreemptive();
274 client.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
275
276
277 return result ;
278
279 }
280 else if (type.startsWith(SSO_TYPE_FORM))
281 {
282 try
283 {
284 Boolean formAuth = (Boolean)PortletMessaging.receive(request, FORM_AUTH_STATE);
285 if (formAuth != null)
286 {
287
288 return (formAuth.booleanValue() ? result : null);
289 }
290 else
291 {
292
293 PortletMessaging.publish(request, FORM_AUTH_STATE, Boolean.FALSE);
294 }
295
296 String formAction = prefs.getValue(SSO_TYPE_FORM_ACTION_URL, "");
297 if (formAction == null || formAction.length() == 0)
298 {
299 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_ACTION_URL+", action was specified - unable to preemptively authenticate by form.");
300 return null ;
301 }
302 String userNameField = prefs.getValue(SSO_TYPE_FORM_USERNAME_FIELD, "");
303 if (userNameField == null || userNameField.length() == 0)
304 {
305 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_USERNAME_FIELD+", username field was specified - unable to preemptively authenticate by form.");
306 return null ;
307 }
308 String passwordField = prefs.getValue(SSO_TYPE_FORM_PASSWORD_FIELD, "password");
309 if (passwordField == null || passwordField.length() == 0)
310 {
311 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_PASSWORD_FIELD+", password field was specified - unable to preemptively authenticate by form.");
312 return null ;
313 }
314
315 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
316 if (userName == null) userName = "";
317 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
318 if (password == null) password = "";
319
320
321 int i = type.indexOf('.');
322 boolean isPost = i > 0 ? type.substring(i+1).equalsIgnoreCase("post") : true ;
323
324
325 HashMap formParams = new HashMap();
326 formParams.put(userNameField,new String[]{ userName });
327 formParams.put(passwordField,new String[]{ password });
328 String formArgs = prefs.getValue(SSO_TYPE_FORM_ACTION_ARGS, "");
329 if (formArgs != null && formArgs.length() > 0)
330 {
331 StringTokenizer iter = new StringTokenizer(formArgs, ";");
332 while (iter.hasMoreTokens())
333 {
334 String pair = iter.nextToken();
335 i = pair.indexOf('=') ;
336 if (i > 0)
337 formParams.put(pair.substring(0,i), new String[]{pair.substring(i+1)});
338 }
339 }
340
341
342 method = getHttpMethod(client, getURLSource(formAction, formParams, request, response), formParams, isPost, request);
343
344 result = doHttpWebContent(client, method, 0, request, response) ;
345
346 PortletMessaging.publish(request, FORM_AUTH_STATE, Boolean.valueOf(result != null));
347 return result ;
348 }
349 catch (Exception ex)
350 {
351
352 log.error("Form-based authentication failed", ex);
353 }
354 }
355 else if (type.equalsIgnoreCase(SSO_TYPE_URL) || type.equalsIgnoreCase(SSO_TYPE_URL_BASE64))
356 {
357
358 String userNameParam = prefs.getValue(SSO_TYPE_URL_USERNAME_PARAM, "");
359 if (userNameParam == null || userNameParam.length() == 0)
360 {
361 log.warn("sso.type specified as 'url', but no: "+SSO_TYPE_URL_USERNAME_PARAM+", username parameter was specified - unable to preemptively authenticate by URL.");
362 return null ;
363 }
364 String passwordParam = prefs.getValue(SSO_TYPE_URL_PASSWORD_PARAM, "");
365 if (passwordParam == null || passwordParam.length() == 0)
366 {
367 log.warn("sso.type specified as 'url', but no: "+SSO_TYPE_URL_PASSWORD_PARAM+", password parameter was specified - unable to preemptively authenticate by URL.");
368 return null ;
369 }
370 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
371 if (userName == null) userName = "";
372 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
373 if (password == null) password = "";
374 if (type.equalsIgnoreCase(SSO_TYPE_URL_BASE64))
375 {
376 Base64 encoder = new Base64() ;
377 userName = new String(encoder.encode(userName.getBytes()));
378 password = new String(encoder.encode(password.getBytes()));
379 }
380
381
382 if ( method instanceof PostMethod )
383 {
384
385 PostMethod postMethod = (PostMethod)method ;
386 postMethod.addParameter(userNameParam, userName);
387 postMethod.addParameter(passwordParam, password);
388 }
389 else
390 {
391
392 NameValuePair[] authPairs = new NameValuePair[]{ new NameValuePair(userNameParam, userName), new NameValuePair(passwordParam, password) } ;
393 String existingQuery = method.getQueryString() ;
394 method.setQueryString(authPairs);
395 if (existingQuery != null && existingQuery.length() > 0)
396 {
397
398 existingQuery = existingQuery + '&' + method.getQueryString();
399 method.setQueryString(existingQuery);
400 }
401 }
402
403 return result ;
404 }
405
406
407
408 return null ;
409 }
410
411 protected boolean doRequestedAuthentication(HttpClient client,HttpMethod method, RenderRequest request, RenderResponse response)
412 {
413 if ( super.doRequestedAuthentication(client, method, request, response))
414 {
415
416 return true ;
417 }
418
419
420
421 if (method.getHostAuthState().getAuthScheme().getSchemeName().equals(BASIC_AUTH_SCHEME_NAME))
422 {
423
424 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
425 if (userName == null) userName = "";
426 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
427 if (password == null) password = "";
428
429
430 method.setDoAuthentication(true);
431 AuthState state = method.getHostAuthState();
432 AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, state.getRealm(), state.getAuthScheme().getSchemeName()) ;
433 client.getState().setCredentials(scope, new UsernamePasswordCredentials(userName, password));
434
435
436 return true ;
437 }
438 else
439 {
440 log.warn("SSOWebContentPortlent.doAuthenticate() - unexpected authentication scheme: "+method.getHostAuthState().getAuthScheme().getSchemeName());
441 }
442
443
444 return false;
445 }
446
447 protected String getSingleSignOnAuthType(PortletPreferences prefs)
448 {
449 String type = prefs.getValue(SSO_TYPE,SSO_TYPE_DEFAULT);
450
451 if (type != null && type.equalsIgnoreCase(SSO_TYPE_HTTP))
452 {
453 log.warn("sso.type: "+SSO_TYPE_HTTP+", has been deprecated - use: "+SSO_TYPE_BASIC+", or: "+SSO_TYPE_BASIC_PREEMPTIVE);
454 type = SSO_TYPE_BASIC ;
455 }
456
457 return type ;
458 }
459 }