1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portlet.login.action;
16  
17  import com.liferay.portal.DuplicateUserEmailAddressException;
18  import com.liferay.portal.NoSuchUserException;
19  import com.liferay.portal.kernel.log.Log;
20  import com.liferay.portal.kernel.log.LogFactoryUtil;
21  import com.liferay.portal.kernel.servlet.SessionErrors;
22  import com.liferay.portal.kernel.servlet.SessionMessages;
23  import com.liferay.portal.kernel.util.CharPool;
24  import com.liferay.portal.kernel.util.Constants;
25  import com.liferay.portal.kernel.util.GetterUtil;
26  import com.liferay.portal.kernel.util.ParamUtil;
27  import com.liferay.portal.kernel.util.StringPool;
28  import com.liferay.portal.kernel.util.Validator;
29  import com.liferay.portal.model.User;
30  import com.liferay.portal.service.ServiceContext;
31  import com.liferay.portal.service.UserLocalServiceUtil;
32  import com.liferay.portal.struts.PortletAction;
33  import com.liferay.portal.theme.ThemeDisplay;
34  import com.liferay.portal.util.OpenIdUtil;
35  import com.liferay.portal.util.PortalUtil;
36  import com.liferay.portal.util.WebKeys;
37  import com.liferay.portlet.ActionResponseImpl;
38  import com.liferay.util.PwdGenerator;
39  
40  import java.util.Calendar;
41  import java.util.List;
42  import java.util.Locale;
43  
44  import javax.portlet.ActionRequest;
45  import javax.portlet.ActionResponse;
46  import javax.portlet.PortletConfig;
47  import javax.portlet.PortletURL;
48  import javax.portlet.RenderRequest;
49  import javax.portlet.RenderResponse;
50  
51  import javax.servlet.http.HttpServletRequest;
52  import javax.servlet.http.HttpServletResponse;
53  import javax.servlet.http.HttpSession;
54  
55  import org.apache.struts.action.ActionForm;
56  import org.apache.struts.action.ActionForward;
57  import org.apache.struts.action.ActionMapping;
58  
59  import org.openid4java.OpenIDException;
60  import org.openid4java.consumer.ConsumerManager;
61  import org.openid4java.consumer.VerificationResult;
62  import org.openid4java.discovery.DiscoveryInformation;
63  import org.openid4java.discovery.Identifier;
64  import org.openid4java.message.AuthRequest;
65  import org.openid4java.message.AuthSuccess;
66  import org.openid4java.message.MessageExtension;
67  import org.openid4java.message.ParameterList;
68  import org.openid4java.message.ax.AxMessage;
69  import org.openid4java.message.ax.FetchRequest;
70  import org.openid4java.message.ax.FetchResponse;
71  import org.openid4java.message.sreg.SRegMessage;
72  import org.openid4java.message.sreg.SRegRequest;
73  import org.openid4java.message.sreg.SRegResponse;
74  
75  /**
76   * <a href="OpenIdAction.java.html"><b><i>View Source</i></b></a>
77   *
78   * @author Brian Wing Shun Chan
79   * @author Jorge Ferrer
80   */
81  public class OpenIdAction extends PortletAction {
82  
83      public void processAction(
84              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
85              ActionRequest actionRequest, ActionResponse actionResponse)
86          throws Exception {
87  
88          ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
89              WebKeys.THEME_DISPLAY);
90  
91          if (actionRequest.getRemoteUser() != null) {
92              actionResponse.sendRedirect(themeDisplay.getPathMain());
93  
94              return;
95          }
96  
97          String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
98  
99          try {
100             if (cmd.equals(Constants.READ)) {
101                 String redirect = readOpenIdResponse(
102                     themeDisplay, actionRequest, actionResponse);
103 
104                 if (Validator.isNull(redirect)) {
105                     redirect =
106                         PortalUtil.getPortalURL(actionRequest) +
107                             themeDisplay.getURLSignIn();
108                 }
109 
110                 sendRedirect(actionRequest, actionResponse, redirect);
111             }
112             else {
113                 sendOpenIdRequest(themeDisplay, actionRequest, actionResponse);
114             }
115         }
116         catch (Exception e) {
117             if (e instanceof DuplicateUserEmailAddressException) {
118                 SessionErrors.add(actionRequest, e.getClass().getName());
119             }
120             else if (e instanceof OpenIDException) {
121                 if (_log.isInfoEnabled()) {
122                     _log.info(
123                         "Error communicating with OpenID provider: " +
124                             e.getMessage());
125                 }
126 
127                 SessionErrors.add(actionRequest, e.getClass().getName());
128             }
129             else {
130                 _log.error("Error processing the OpenID login", e);
131 
132                 PortalUtil.sendError(e, actionRequest, actionResponse);
133             }
134         }
135     }
136 
137     public ActionForward render(
138             ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
139             RenderRequest renderRequest, RenderResponse renderResponse)
140         throws Exception {
141 
142         ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
143             WebKeys.THEME_DISPLAY);
144 
145         renderResponse.setTitle(themeDisplay.translate("open-id"));
146 
147         return mapping.findForward("portlet.login.open_id");
148     }
149 
150     protected String getFirstValue(List<String> values) {
151         if ((values == null) || (values.size() < 1)) {
152             return null;
153         }
154 
155         return values.get(0);
156     }
157 
158     protected boolean isCheckMethodOnProcessAction() {
159         return _CHECK_METHOD_ON_PROCESS_ACTION;
160     }
161 
162     protected String readOpenIdResponse(
163             ThemeDisplay themeDisplay, ActionRequest actionRequest,
164             ActionResponse actionResponse)
165         throws Exception {
166 
167         HttpServletRequest request = PortalUtil.getHttpServletRequest(
168             actionRequest);
169         HttpSession session = request.getSession();
170 
171         ConsumerManager manager = OpenIdUtil.getConsumerManager();
172 
173         ParameterList params = new ParameterList(
174             actionRequest.getParameterMap());
175 
176         DiscoveryInformation discovered =
177             (DiscoveryInformation)session.getAttribute(WebKeys.OPEN_ID_DISCO);
178 
179         if (discovered == null) {
180             return null;
181         }
182 
183         String receivingUrl = ParamUtil.getString(
184             actionRequest, "openid.return_to");
185 
186         VerificationResult verification = manager.verify(
187             receivingUrl, params, discovered);
188 
189         Identifier verified = verification.getVerifiedId();
190 
191         if (verified == null) {
192             return null;
193         }
194 
195         AuthSuccess authSuccess = (AuthSuccess)verification.getAuthResponse();
196 
197         String firstName = null;
198         String lastName = null;
199         String emailAddress = null;
200 
201         if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) {
202             MessageExtension ext = authSuccess.getExtension(
203                 SRegMessage.OPENID_NS_SREG);
204 
205             if (ext instanceof SRegResponse) {
206                 SRegResponse sregResp = (SRegResponse)ext;
207 
208                 String fullName = GetterUtil.getString(
209                     sregResp.getAttributeValue("fullname"));
210 
211                 int pos = fullName.indexOf(CharPool.SPACE);
212 
213                 if ((pos != -1) && ((pos + 1) < fullName.length())) {
214                     firstName = fullName.substring(0, pos);
215                     lastName = fullName.substring(pos + 1);
216                 }
217 
218                 emailAddress = sregResp.getAttributeValue("email");
219             }
220         }
221 
222         if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
223             MessageExtension ext = authSuccess.getExtension(
224                 AxMessage.OPENID_NS_AX);
225 
226             if (ext instanceof FetchResponse) {
227                 FetchResponse fetchResp = (FetchResponse)ext;
228 
229                 if (Validator.isNull(firstName)) {
230                     firstName = getFirstValue(
231                         fetchResp.getAttributeValues("firstName"));
232                 }
233 
234                 if (Validator.isNull(lastName)) {
235                     lastName = getFirstValue(
236                         fetchResp.getAttributeValues("lastName"));
237                 }
238 
239                 if (Validator.isNull(emailAddress)) {
240                     emailAddress = getFirstValue(
241                         fetchResp.getAttributeValues("email"));
242                 }
243             }
244         }
245 
246         String openId = OpenIdUtil.normalize(authSuccess.getIdentity());
247 
248         User user = null;
249 
250         try {
251             user = UserLocalServiceUtil.getUserByOpenId(openId);
252         }
253         catch (NoSuchUserException nsue) {
254             if (Validator.isNull(firstName) || Validator.isNull(lastName) ||
255                 Validator.isNull(emailAddress)) {
256 
257                 SessionMessages.add(request, "missingOpenIdUserInformation");
258 
259                 if (_log.isInfoEnabled()) {
260                     _log.info(
261                         "The OpenID provider did not send the required " +
262                             "attributes to create an account");
263                 }
264 
265                 PortletURL createAccountURL =
266                     themeDisplay.getURLCreateAccount();
267 
268                 createAccountURL.setParameter("openId", openId);
269 
270                 session.setAttribute(
271                     WebKeys.OPEN_ID_LOGIN_PENDING, Boolean.TRUE);
272 
273                 return createAccountURL.toString();
274             }
275 
276             long creatorUserId = 0;
277             long companyId = themeDisplay.getCompanyId();
278             boolean autoPassword = false;
279             String password1 = PwdGenerator.getPassword();
280             String password2 = password1;
281             boolean autoScreenName = true;
282             String screenName = StringPool.BLANK;
283             Locale locale = themeDisplay.getLocale();
284             String middleName = StringPool.BLANK;
285             int prefixId = 0;
286             int suffixId = 0;
287             boolean male = true;
288             int birthdayMonth = Calendar.JANUARY;
289             int birthdayDay = 1;
290             int birthdayYear = 1970;
291             String jobTitle = StringPool.BLANK;
292             long[] groupIds = null;
293             long[] organizationIds = null;
294             long[] roleIds = null;
295             long[] userGroupIds = null;
296             boolean sendEmail = false;
297 
298             ServiceContext serviceContext = new ServiceContext();
299 
300             user = UserLocalServiceUtil.addUser(
301                 creatorUserId, companyId, autoPassword, password1, password2,
302                 autoScreenName, screenName, emailAddress, openId, locale,
303                 firstName, middleName, lastName, prefixId, suffixId, male,
304                 birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds,
305                 organizationIds, roleIds, userGroupIds, sendEmail,
306                 serviceContext);
307         }
308 
309         session.setAttribute(WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
310 
311         return null;
312     }
313 
314     protected void sendOpenIdRequest(
315             ThemeDisplay themeDisplay, ActionRequest actionRequest,
316             ActionResponse actionResponse)
317         throws Exception {
318 
319         if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
320             return;
321         }
322 
323         HttpServletRequest request = PortalUtil.getHttpServletRequest(
324             actionRequest);
325         HttpServletResponse response = PortalUtil.getHttpServletResponse(
326             actionResponse);
327         HttpSession session = request.getSession();
328 
329         ActionResponseImpl actionResponseImpl =
330             (ActionResponseImpl)actionResponse;
331 
332         String openId = ParamUtil.getString(actionRequest, "openId");
333 
334         PortletURL portletURL = actionResponseImpl.createActionURL();
335 
336         portletURL.setParameter("struts_action", "/login/open_id");
337         portletURL.setParameter(Constants.CMD, Constants.READ);
338         portletURL.setParameter("saveLastPath", "0");
339 
340         ConsumerManager manager = OpenIdUtil.getConsumerManager();
341 
342         List<DiscoveryInformation> discoveries = manager.discover(openId);
343 
344         DiscoveryInformation discovered = manager.associate(discoveries);
345 
346         session.setAttribute(WebKeys.OPEN_ID_DISCO, discovered);
347 
348         AuthRequest authRequest = manager.authenticate(
349             discovered, portletURL.toString(), themeDisplay.getPortalURL());
350 
351         try {
352             UserLocalServiceUtil.getUserByOpenId(openId);
353         }
354         catch (NoSuchUserException nsue) {
355             String screenName = OpenIdUtil.getScreenName(openId);
356 
357             try {
358                 User user = UserLocalServiceUtil.getUserByScreenName(
359                     themeDisplay.getCompanyId(), screenName);
360 
361                 UserLocalServiceUtil.updateOpenId(user.getUserId(), openId);
362             }
363             catch (NoSuchUserException nsue2) {
364                 FetchRequest fetch = FetchRequest.createFetchRequest();
365 
366                 fetch.addAttribute(
367                     "email", "http://schema.openid.net/contact/email", true);
368                 fetch.addAttribute(
369                     "firstName", "http://schema.openid.net/namePerson/first",
370                     true);
371                 fetch.addAttribute(
372                     "lastName", "http://schema.openid.net/namePerson/last",
373                     true);
374 
375                 authRequest.addExtension(fetch);
376 
377                 SRegRequest sregRequest = SRegRequest.createFetchRequest();
378 
379                 sregRequest.addAttribute("fullname", true);
380                 sregRequest.addAttribute("email", true);
381 
382                 authRequest.addExtension(sregRequest);
383             }
384         }
385 
386         response.sendRedirect(authRequest.getDestinationUrl(true));
387     }
388 
389     private static final boolean _CHECK_METHOD_ON_PROCESS_ACTION = false;
390 
391     private static Log _log = LogFactoryUtil.getLog(OpenIdAction.class);
392 
393 }