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 
316             ServiceContext serviceContext = new ServiceContext();
317 
318             user = UserLocalServiceUtil.addUser(
319                 creatorUserId, companyId, autoPassword, password1, password2,
320                 autoScreenName, screenName, emailAddress, openId, locale,
321                 firstName, middleName, lastName, prefixId, suffixId, male,
322                 birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds,
323                 organizationIds, roleIds, userGroupIds, sendEmail,
324                 serviceContext);
325         }
326 
327         session.setAttribute(WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
328 
329         return null;
330     }
331 
332     protected void sendOpenIdRequest(
333             ThemeDisplay themeDisplay, ActionRequest actionRequest,
334             ActionResponse actionResponse)
335         throws Exception {
336 
337         if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
338             return;
339         }
340 
341         HttpServletRequest request = PortalUtil.getHttpServletRequest(
342             actionRequest);
343         HttpServletResponse response = PortalUtil.getHttpServletResponse(
344             actionResponse);
345         HttpSession session = request.getSession();
346 
347         ActionResponseImpl actionResponseImpl =
348             (ActionResponseImpl)actionResponse;
349 
350         String openId = ParamUtil.getString(actionRequest, "openId");
351 
352         PortletURL portletURL = actionResponseImpl.createActionURL();
353 
354         portletURL.setParameter("struts_action", "/login/open_id");
355         portletURL.setParameter(Constants.CMD, Constants.READ);
356         portletURL.setParameter("saveLastPath", "0");
357 
358         ConsumerManager manager = OpenIdUtil.getConsumerManager();
359 
360         List<DiscoveryInformation> discoveries = manager.discover(openId);
361 
362         DiscoveryInformation discovered = manager.associate(discoveries);
363 
364         session.setAttribute(WebKeys.OPEN_ID_DISCO, discovered);
365 
366         AuthRequest authRequest = manager.authenticate(
367             discovered, portletURL.toString(), themeDisplay.getPortalURL());
368 
369         try {
370             UserLocalServiceUtil.getUserByOpenId(openId);
371         }
372         catch (NoSuchUserException nsue) {
373             String screenName = OpenIdUtil.getScreenName(openId);
374 
375             try {
376                 User user = UserLocalServiceUtil.getUserByScreenName(
377                     themeDisplay.getCompanyId(), screenName);
378 
379                 UserLocalServiceUtil.updateOpenId(user.getUserId(), openId);
380             }
381             catch (NoSuchUserException nsue2) {
382                 FetchRequest fetch = FetchRequest.createFetchRequest();
383 
384                 fetch.addAttribute(
385                     "email", "http://schema.openid.net/contact/email", true);
386                 fetch.addAttribute(
387                     "firstName", "http://schema.openid.net/namePerson/first",
388                     true);
389                 fetch.addAttribute(
390                     "lastName", "http://schema.openid.net/namePerson/last",
391                     true);
392 
393                 authRequest.addExtension(fetch);
394 
395                 SRegRequest sregRequest = SRegRequest.createFetchRequest();
396 
397                 sregRequest.addAttribute("fullname", true);
398                 sregRequest.addAttribute("email", true);
399 
400                 authRequest.addExtension(sregRequest);
401             }
402         }
403 
404         response.sendRedirect(authRequest.getDestinationUrl(true));
405     }
406 
407     private static final boolean _CHECK_METHOD_ON_PROCESS_ACTION = false;
408 
409     private static Log _log = LogFactoryUtil.getLog(OpenIdAction.class);
410 
411 }