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