001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /*Created on: Dec 5, 2005 */
019:
020: package org.apache.jetspeed.portlet.sso;
021:
022: import java.io.IOException;
023: import java.security.AccessControlContext;
024: import java.security.AccessController;
025: import javax.portlet.ActionRequest;
026: import javax.portlet.ActionResponse;
027: import javax.portlet.PortletConfig;
028: import javax.portlet.PortletContext;
029: import javax.portlet.PortletException;
030: import javax.portlet.PortletURL;
031: import javax.portlet.RenderRequest;
032: import javax.portlet.RenderResponse;
033: import javax.security.auth.Subject;
034:
035: import org.apache.jetspeed.security.JSSubject;
036: import org.apache.jetspeed.sso.SSOException;
037: import org.apache.jetspeed.sso.SSOProvider;
038: import org.apache.portals.bridges.common.ScriptPostProcess;
039: import org.apache.portals.bridges.velocity.GenericVelocityPortlet;
040:
041: /**
042: * SSOProxyPortlet
043: * This portlet can be used as a bridge to any URL.
044: * It acts as a http client and therefore it can store
045: * cookies.
046: * The main purpose is that the SSOProxy portlet authenticates
047: * any SSO credential for the principal user not knowing in advance
048: * what URL the user might select. No login prompt will appear for any url
049: * in the portlet for that an SSO entry exists and the principal user has permissions.
050: *
051: * @author Roger Ruttimann <rogerrut@apache.org>
052: *
053: */
054: public class SSOProxyPortlet extends GenericVelocityPortlet {
055: private PortletContext context;
056: private SSOProvider sso;
057:
058: /* Re-use Proxy client inside the SSO Component */
059: private boolean isAuthenticated = false;
060:
061: /** Default encoding UTF-8*/
062: public String defaultEncoding = "UTF-8";
063:
064: /** Block Size */
065: static final int BLOCK_SIZE = 4096;
066:
067: /** ACTION_PARAMETER_SSOPROXY*/
068: static final String ACTION_PARAMETER_SSOPROXY = "SSOProxy";
069:
070: /** Preference values */
071: /** DestinationURL */
072: static final String DESTINATION_URL = "DestinationURL";
073:
074: /** SSOSite */
075: static final String SSO_SITE = "SSOSite";
076:
077: /** ForceSSORefresh*/
078: static final String FORCE_SSO_REFRESH = "ForceSSORefresh";
079:
080: /** Encoding*/
081: static final String ENCODING = "Encoding";
082:
083: private String destinationURL;
084: private String ssoSite;
085: private String encoding;
086:
087: public void init(PortletConfig config) throws PortletException {
088: super .init(config);
089: context = getPortletContext();
090: sso = (SSOProvider) context.getAttribute("cps:SSO");
091: if (null == sso) {
092: throw new PortletException(
093: "Failed to find SSO Provider on portlet initialization");
094: }
095:
096: }
097:
098: public void processAction(ActionRequest request,
099: ActionResponse actionResponse) throws PortletException,
100: IOException {
101: String ssoProxyAction = request
102: .getParameter(ACTION_PARAMETER_SSOPROXY);
103: // System.out.println("SSOProxy Action value [" + ssoProxyAction + "]");
104:
105: if (ssoProxyAction != null && ssoProxyAction.length() > 0)
106: this .destinationURL = ssoProxyAction;
107: else
108: this .destinationURL = request.getParameter(DESTINATION_URL);
109:
110: this .ssoSite = request.getParameter(SSO_SITE);
111: this .encoding = request.getParameter(ENCODING);
112: if (this .encoding == null)
113: this .encoding = this .defaultEncoding;
114:
115: // save the prefs
116: super .processAction(request, actionResponse);
117: }
118:
119: public void doView(RenderRequest request, RenderResponse response)
120: throws PortletException, IOException {
121: String forceRefresh = request.getPreferences().getValue(
122: FORCE_SSO_REFRESH, "false");
123:
124: if (destinationURL == null || destinationURL.length() == 0) {
125: // No destination configured Switch to configure View
126: request.setAttribute(PARAM_VIEW_PAGE, this
127: .getPortletConfig().getInitParameter(
128: PARAM_EDIT_PAGE));
129: setupPreferencesEdit(request, response);
130: super .doView(request, response);
131: return;
132: }
133:
134: // Set the content type
135: response.setContentType("text/html");
136:
137: /*
138: * Call into the SSO Proxy and process the result page
139: */
140: boolean doRefresh = false;
141: if ((forceRefresh.compareToIgnoreCase("TRUE") == 0)
142: || this .isAuthenticated == false)
143: doRefresh = true;
144:
145: try {
146: StringBuffer page = new StringBuffer();
147: Subject subject = getSubject();
148: if (ssoSite == null || ssoSite.length() == 0)
149: page.append(sso.useSSO(subject, destinationURL,
150: doRefresh));
151: else
152: page.append(sso.useSSO(subject, destinationURL,
153: ssoSite, doRefresh));
154:
155: // Authentication done at least once
156: this .isAuthenticated = true;
157: /*
158: bis.mark(BLOCK_SIZE);
159: String pageEncoding = getContentCharSet(bis);
160: if (pageEncoding == null)
161: {
162: pageEncoding = encoding;
163: }
164:
165: Reader read = new InputStreamReader(bis, encoding);
166:
167:
168: char[] bytes = new char[BLOCK_SIZE];
169:
170: int len = read.read(bytes, 0, BLOCK_SIZE);
171: while (len > 0)
172: {
173: page.append(bytes, 0, len);
174: len = read.read(bytes, 0, BLOCK_SIZE);
175: }
176:
177: //Done
178: read.close();
179: */
180: // Rewrite
181: // Post Process for generated page
182: PortletURL actionURL = response.createActionURL();
183: ScriptPostProcess processor = new ScriptPostProcess();
184: processor.setInitalPage(page);
185: processor.postProcessPage(actionURL,
186: ACTION_PARAMETER_SSOPROXY);
187: String finalPage = processor.getFinalizedPage();
188:
189: // Write the page
190: response.getWriter().println(finalPage);
191:
192: } catch (SSOException e) {
193: response.getWriter().println(
194: "<P>Error rendering page. Error message<BR>"
195: + e.getMessage() + "</P>");
196:
197: this .destinationURL = "";
198: }
199: }
200:
201: public void doEdit(RenderRequest request, RenderResponse response)
202: throws PortletException, IOException {
203: super .doEdit(request, response);
204: }
205:
206: /*
207: * Helper methods
208: */
209: private Subject getSubject() {
210: AccessControlContext context = AccessController.getContext();
211: return JSSubject.getSubject(context);
212: }
213:
214: /*
215: private String getContentCharSet(InputStream is) throws IOException
216: {
217: if (!is.markSupported())
218: {
219: return null;
220: }
221:
222: byte[] buf = new byte[BLOCK_SIZE];
223: try
224: {
225: is.read(buf, 0, BLOCK_SIZE);
226: String content = new String(buf, "ISO-8859-1");
227: String lowerCaseContent = content.toLowerCase();
228: int startIndex = lowerCaseContent.indexOf("<head");
229: if (startIndex == -1)
230: {
231: startIndex = 0;
232: }
233: int endIndex = lowerCaseContent.indexOf("</head");
234: if (endIndex == -1)
235: {
236: endIndex = content.length();
237: }
238: content = content.substring(startIndex, endIndex);
239:
240: StringTokenizer st = new StringTokenizer(content, "<>");
241: while (st.hasMoreTokens())
242: {
243: String element = st.nextToken();
244: String lowerCaseElement = element.toLowerCase();
245: if (lowerCaseElement.startsWith("meta") && lowerCaseElement.indexOf("content-type") > 0)
246: {
247: StringTokenizer est = new StringTokenizer(element, " =\"\';");
248: while (est.hasMoreTokens())
249: {
250: if (est.nextToken().equalsIgnoreCase("charset"))
251: {
252: if (est.hasMoreTokens())
253: {
254: is.reset();
255: return est.nextToken();
256: }
257: }
258: }
259: }
260: }
261: }
262: catch (IOException e)
263: {
264: }
265:
266: is.reset();
267:
268: return null;
269: }
270: */
271:
272: }
|