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