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 605431 2007-12-19 05:11:40Z taylor $
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 protected PortletContext context;
124 protected 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 SSOContext context = sso.getCredentials(subject, site);
164 if (!context.getRemotePrincipalName().equals(ssoPrincipal))
165 {
166 sso.removeCredentialsForSite(subject, site);
167 sso.addCredentialsForSite(subject, ssoPrincipal, site, ssoCredential);
168 }
169 else
170 {
171 sso.updateCredentialsForSite(subject, ssoPrincipal, site, ssoCredential);
172 }
173 }
174 else
175 {
176 sso.addCredentialsForSite(subject, ssoPrincipal, site, ssoCredential);
177 }
178 }
179 catch (SSOException e)
180 {
181 throw new PortletException(e);
182 }
183 }
184 }
185
186 public void doView(RenderRequest request, RenderResponse response)
187 throws PortletException, IOException
188 {
189 String site = request.getPreferences().getValue("SRC", null);
190
191 if (site == null)
192 {
193
194 request.setAttribute(PARAM_VIEW_PAGE, this.getPortletConfig().getInitParameter(PARAM_EDIT_PAGE));
195 setupPreferencesEdit(request, response);
196 }
197 else try
198 {
199 Subject subject = getSubject();
200 SSOContext context = sso.getCredentials(subject, site);
201 request.setAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME, context.getRemotePrincipalName());
202 request.setAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD, context.getRemoteCredential());
203 }
204 catch (SSOException e)
205 {
206 if (e.getMessage().equals(SSOException.NO_CREDENTIALS_FOR_SITE))
207 {
208
209
210 request.setAttribute(PARAM_VIEW_PAGE, this.getPortletConfig().getInitParameter(PARAM_EDIT_PAGE));
211 setupPreferencesEdit(request, response);
212 }
213 else
214 {
215 throw new PortletException(e);
216 }
217 }
218
219 super.doView(request, response);
220 }
221
222
223 public void doEdit(RenderRequest request, RenderResponse response)
224 throws PortletException, IOException
225 {
226 try
227 {
228 Subject subject = getSubject();
229 String site = request.getPreferences().getValue("SRC", "");
230 SSOContext context = sso.getCredentials(subject, site);
231 getContext(request).put(SSO_EDIT_FIELD_PRINCIPAL, context.getRemotePrincipalName());
232 getContext(request).put(SSO_EDIT_FIELD_CREDENTIAL, context.getRemoteCredential());
233 }
234 catch (SSOException e)
235 {
236 if (e.getMessage().equals(SSOException.NO_CREDENTIALS_FOR_SITE))
237 {
238
239
240 getContext(request).put(SSO_EDIT_FIELD_PRINCIPAL, "");
241 getContext(request).put(SSO_EDIT_FIELD_CREDENTIAL, "");
242 }
243 else
244 {
245 throw new PortletException(e);
246 }
247 }
248
249 super.doEdit(request, response);
250 }
251
252 private Subject getSubject()
253 {
254 AccessControlContext context = AccessController.getContext();
255 return JSSubject.getSubject(context);
256 }
257
258 protected byte[] doPreemptiveAuthentication(HttpClient client,HttpMethod method, RenderRequest request, RenderResponse response)
259 {
260 byte[] result = super.doPreemptiveAuthentication(client, method, request, response);
261 if ( result != null)
262 {
263
264 return result ;
265 }
266
267
268
269 PortletPreferences prefs = request.getPreferences();
270 String type = getSingleSignOnAuthType(prefs);
271
272 if (type.equalsIgnoreCase(SSO_TYPE_BASIC_PREEMPTIVE))
273 {
274
275 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
276 if (userName == null) userName = "";
277 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
278 if (password == null) password = "";
279
280
281 method.setDoAuthentication(true);
282 method.getHostAuthState().setPreemptive();
283 client.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));
284
285
286 return result ;
287
288 }
289 else if (type.startsWith(SSO_TYPE_FORM))
290 {
291 try
292 {
293 Boolean formAuth = (Boolean)PortletMessaging.receive(request, FORM_AUTH_STATE);
294 if (formAuth != null)
295 {
296
297 return (formAuth.booleanValue() ? result : null);
298 }
299 else
300 {
301
302 PortletMessaging.publish(request, FORM_AUTH_STATE, Boolean.FALSE);
303 }
304
305 String formAction = prefs.getValue(SSO_TYPE_FORM_ACTION_URL, "");
306 if (formAction == null || formAction.length() == 0)
307 {
308 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_ACTION_URL+", action was specified - unable to preemptively authenticate by form.");
309 return null ;
310 }
311 String userNameField = prefs.getValue(SSO_TYPE_FORM_USERNAME_FIELD, "");
312 if (userNameField == null || userNameField.length() == 0)
313 {
314 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_USERNAME_FIELD+", username field was specified - unable to preemptively authenticate by form.");
315 return null ;
316 }
317 String passwordField = prefs.getValue(SSO_TYPE_FORM_PASSWORD_FIELD, "password");
318 if (passwordField == null || passwordField.length() == 0)
319 {
320 log.warn("sso.type specified as 'form', but no: "+SSO_TYPE_FORM_PASSWORD_FIELD+", password field was specified - unable to preemptively authenticate by form.");
321 return null ;
322 }
323
324 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
325 if (userName == null) userName = "";
326 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
327 if (password == null) password = "";
328
329
330 int i = type.indexOf('.');
331 boolean isPost = i > 0 ? type.substring(i+1).equalsIgnoreCase("post") : true ;
332
333
334 HashMap formParams = new HashMap();
335 formParams.put(userNameField,new String[]{ userName });
336 formParams.put(passwordField,new String[]{ password });
337 String formArgs = prefs.getValue(SSO_TYPE_FORM_ACTION_ARGS, "");
338 if (formArgs != null && formArgs.length() > 0)
339 {
340 StringTokenizer iter = new StringTokenizer(formArgs, ";");
341 while (iter.hasMoreTokens())
342 {
343 String pair = iter.nextToken();
344 i = pair.indexOf('=') ;
345 if (i > 0)
346 formParams.put(pair.substring(0,i), new String[]{pair.substring(i+1)});
347 }
348 }
349
350
351 String formMethod = (isPost) ? FORM_POST_METHOD : FORM_GET_METHOD;
352 method = getHttpMethod(client, getURLSource(formAction, formParams, request, response), formParams, formMethod, request);
353
354 result = doHttpWebContent(client, method, 0, request, response) ;
355
356 PortletMessaging.publish(request, FORM_AUTH_STATE, Boolean.valueOf(result != null));
357 return result ;
358 }
359 catch (Exception ex)
360 {
361
362 log.error("Form-based authentication failed", ex);
363 }
364 }
365 else if (type.equalsIgnoreCase(SSO_TYPE_URL) || type.equalsIgnoreCase(SSO_TYPE_URL_BASE64))
366 {
367
368 String userNameParam = prefs.getValue(SSO_TYPE_URL_USERNAME_PARAM, "");
369 if (userNameParam == null || userNameParam.length() == 0)
370 {
371 log.warn("sso.type specified as 'url', but no: "+SSO_TYPE_URL_USERNAME_PARAM+", username parameter was specified - unable to preemptively authenticate by URL.");
372 return null ;
373 }
374 String passwordParam = prefs.getValue(SSO_TYPE_URL_PASSWORD_PARAM, "");
375 if (passwordParam == null || passwordParam.length() == 0)
376 {
377 log.warn("sso.type specified as 'url', but no: "+SSO_TYPE_URL_PASSWORD_PARAM+", password parameter was specified - unable to preemptively authenticate by URL.");
378 return null ;
379 }
380 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
381 if (userName == null) userName = "";
382 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
383 if (password == null) password = "";
384 if (type.equalsIgnoreCase(SSO_TYPE_URL_BASE64))
385 {
386 Base64 encoder = new Base64() ;
387 userName = new String(encoder.encode(userName.getBytes()));
388 password = new String(encoder.encode(password.getBytes()));
389 }
390
391
392 if ( method instanceof PostMethod )
393 {
394
395 PostMethod postMethod = (PostMethod)method ;
396 postMethod.addParameter(userNameParam, userName);
397 postMethod.addParameter(passwordParam, password);
398 }
399 else
400 {
401
402 NameValuePair[] authPairs = new NameValuePair[]{ new NameValuePair(userNameParam, userName), new NameValuePair(passwordParam, password) } ;
403 String existingQuery = method.getQueryString() ;
404 method.setQueryString(authPairs);
405 if (existingQuery != null && existingQuery.length() > 0)
406 {
407
408 existingQuery = existingQuery + '&' + method.getQueryString();
409 method.setQueryString(existingQuery);
410 }
411 }
412
413 return result ;
414 }
415
416
417
418 return null ;
419 }
420
421 protected boolean doRequestedAuthentication(HttpClient client,HttpMethod method, RenderRequest request, RenderResponse response)
422 {
423 if ( super.doRequestedAuthentication(client, method, request, response))
424 {
425
426 return true ;
427 }
428
429
430
431 if (method.getHostAuthState().getAuthScheme().getSchemeName().equals(BASIC_AUTH_SCHEME_NAME))
432 {
433
434 String userName = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_USERNAME);
435 if (userName == null) userName = "";
436 String password = (String)request.getAttribute(SSO_REQUEST_ATTRIBUTE_PASSWORD);
437 if (password == null) password = "";
438
439
440 method.setDoAuthentication(true);
441 AuthState state = method.getHostAuthState();
442 AuthScope scope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, state.getRealm(), state.getAuthScheme().getSchemeName()) ;
443 client.getState().setCredentials(scope, new UsernamePasswordCredentials(userName, password));
444
445
446 return true ;
447 }
448 else
449 {
450 log.warn("SSOWebContentPortlent.doAuthenticate() - unexpected authentication scheme: "+method.getHostAuthState().getAuthScheme().getSchemeName());
451 }
452
453
454 return false;
455 }
456
457 protected String getSingleSignOnAuthType(PortletPreferences prefs)
458 {
459 String type = prefs.getValue(SSO_TYPE,SSO_TYPE_DEFAULT);
460
461 if (type != null && type.equalsIgnoreCase(SSO_TYPE_HTTP))
462 {
463 log.warn("sso.type: "+SSO_TYPE_HTTP+", has been deprecated - use: "+SSO_TYPE_BASIC+", or: "+SSO_TYPE_BASIC_PREEMPTIVE);
464 type = SSO_TYPE_BASIC ;
465 }
466
467 return type ;
468 }
469 }