001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.login.action;
016    
017    import com.liferay.portal.DuplicateUserEmailAddressException;
018    import com.liferay.portal.NoSuchUserException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.servlet.SessionErrors;
022    import com.liferay.portal.kernel.servlet.SessionMessages;
023    import com.liferay.portal.kernel.util.CharPool;
024    import com.liferay.portal.kernel.util.Constants;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.HttpUtil;
027    import com.liferay.portal.kernel.util.ParamUtil;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.model.User;
031    import com.liferay.portal.security.auth.PrincipalException;
032    import com.liferay.portal.service.ServiceContext;
033    import com.liferay.portal.service.UserLocalServiceUtil;
034    import com.liferay.portal.struts.PortletAction;
035    import com.liferay.portal.theme.ThemeDisplay;
036    import com.liferay.portal.util.OpenIdUtil;
037    import com.liferay.portal.util.PortalUtil;
038    import com.liferay.portal.util.WebKeys;
039    import com.liferay.portlet.ActionResponseImpl;
040    import com.liferay.util.PwdGenerator;
041    
042    import java.util.Calendar;
043    import java.util.List;
044    import java.util.Locale;
045    
046    import javax.portlet.ActionRequest;
047    import javax.portlet.ActionResponse;
048    import javax.portlet.PortletConfig;
049    import javax.portlet.PortletURL;
050    import javax.portlet.RenderRequest;
051    import javax.portlet.RenderResponse;
052    
053    import javax.servlet.http.HttpServletRequest;
054    import javax.servlet.http.HttpServletResponse;
055    import javax.servlet.http.HttpSession;
056    
057    import org.apache.struts.action.ActionForm;
058    import org.apache.struts.action.ActionForward;
059    import org.apache.struts.action.ActionMapping;
060    
061    import org.openid4java.OpenIDException;
062    import org.openid4java.consumer.ConsumerManager;
063    import org.openid4java.consumer.VerificationResult;
064    import org.openid4java.discovery.DiscoveryInformation;
065    import org.openid4java.discovery.Identifier;
066    import org.openid4java.message.AuthRequest;
067    import org.openid4java.message.AuthSuccess;
068    import org.openid4java.message.MessageExtension;
069    import org.openid4java.message.ParameterList;
070    import org.openid4java.message.ax.AxMessage;
071    import org.openid4java.message.ax.FetchRequest;
072    import org.openid4java.message.ax.FetchResponse;
073    import org.openid4java.message.sreg.SRegMessage;
074    import org.openid4java.message.sreg.SRegRequest;
075    import org.openid4java.message.sreg.SRegResponse;
076    
077    /**
078     * @author Brian Wing Shun Chan
079     * @author Jorge Ferrer
080     */
081    public class OpenIdAction extends PortletAction {
082    
083            @Override
084            public void processAction(
085                            ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
086                            ActionRequest actionRequest, ActionResponse actionResponse)
087                    throws Exception {
088    
089                    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
090                            WebKeys.THEME_DISPLAY);
091    
092                    if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
093                            throw new PrincipalException();
094                    }
095    
096                    if (actionRequest.getRemoteUser() != null) {
097                            actionResponse.sendRedirect(themeDisplay.getPathMain());
098    
099                            return;
100                    }
101    
102                    String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
103    
104                    try {
105                            if (cmd.equals(Constants.READ)) {
106                                    String redirect = readOpenIdResponse(
107                                            themeDisplay, actionRequest, actionResponse);
108    
109                                    if (Validator.isNull(redirect)) {
110                                            redirect =
111                                                    PortalUtil.getPortalURL(actionRequest) +
112                                                            themeDisplay.getURLSignIn();
113                                    }
114    
115                                    sendRedirect(actionRequest, actionResponse, redirect);
116                            }
117                            else {
118                                    sendOpenIdRequest(themeDisplay, actionRequest, actionResponse);
119                            }
120                    }
121                    catch (Exception e) {
122                            if (e instanceof DuplicateUserEmailAddressException) {
123                                    SessionErrors.add(actionRequest, e.getClass().getName());
124                            }
125                            else if (e instanceof OpenIDException) {
126                                    if (_log.isInfoEnabled()) {
127                                            _log.info(
128                                                    "Error communicating with OpenID provider: " +
129                                                            e.getMessage());
130                                    }
131    
132                                    SessionErrors.add(actionRequest, e.getClass().getName());
133                            }
134                            else {
135                                    _log.error("Error processing the OpenID login", e);
136    
137                                    PortalUtil.sendError(e, actionRequest, actionResponse);
138                            }
139                    }
140            }
141    
142            @Override
143            public ActionForward render(
144                            ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
145                            RenderRequest renderRequest, RenderResponse renderResponse)
146                    throws Exception {
147    
148                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
149                            WebKeys.THEME_DISPLAY);
150    
151                    if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
152                            return mapping.findForward("portlet.login.login");
153                    }
154    
155                    renderResponse.setTitle(themeDisplay.translate("open-id"));
156    
157                    return mapping.findForward("portlet.login.open_id");
158            }
159    
160            protected String getFirstValue(List<String> values) {
161                    if ((values == null) || (values.size() < 1)) {
162                            return null;
163                    }
164    
165                    return values.get(0);
166            }
167    
168            @Override
169            protected boolean isCheckMethodOnProcessAction() {
170                    return _CHECK_METHOD_ON_PROCESS_ACTION;
171            }
172    
173            protected String readOpenIdResponse(
174                            ThemeDisplay themeDisplay, ActionRequest actionRequest,
175                            ActionResponse actionResponse)
176                    throws Exception {
177    
178                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
179                            actionRequest);
180                    HttpSession session = request.getSession();
181    
182                    ConsumerManager manager = OpenIdUtil.getConsumerManager();
183    
184                    ParameterList params = new ParameterList(
185                            actionRequest.getParameterMap());
186    
187                    DiscoveryInformation discovered =
188                            (DiscoveryInformation)session.getAttribute(WebKeys.OPEN_ID_DISCO);
189    
190                    if (discovered == null) {
191                            return null;
192                    }
193    
194                    String receivingUrl = ParamUtil.getString(
195                            actionRequest, "openid.return_to");
196    
197                    VerificationResult verification = manager.verify(
198                            receivingUrl, params, discovered);
199    
200                    Identifier verified = verification.getVerifiedId();
201    
202                    if (verified == null) {
203                            return null;
204                    }
205    
206                    AuthSuccess authSuccess = (AuthSuccess)verification.getAuthResponse();
207    
208                    String firstName = null;
209                    String lastName = null;
210                    String emailAddress = null;
211    
212                    if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) {
213                            MessageExtension ext = authSuccess.getExtension(
214                                    SRegMessage.OPENID_NS_SREG);
215    
216                            if (ext instanceof SRegResponse) {
217                                    SRegResponse sregResp = (SRegResponse)ext;
218    
219                                    String fullName = GetterUtil.getString(
220                                            sregResp.getAttributeValue("fullname"));
221    
222                                    int pos = fullName.indexOf(CharPool.SPACE);
223    
224                                    if ((pos != -1) && ((pos + 1) < fullName.length())) {
225                                            firstName = fullName.substring(0, pos);
226                                            lastName = fullName.substring(pos + 1);
227                                    }
228    
229                                    emailAddress = sregResp.getAttributeValue("email");
230                            }
231                    }
232    
233                    if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
234                            MessageExtension ext = authSuccess.getExtension(
235                                    AxMessage.OPENID_NS_AX);
236    
237                            if (ext instanceof FetchResponse) {
238                                    FetchResponse fetchResp = (FetchResponse)ext;
239    
240                                    if (Validator.isNull(firstName)) {
241                                            firstName = getFirstValue(
242                                                    fetchResp.getAttributeValues("firstName"));
243                                    }
244    
245                                    if (Validator.isNull(lastName)) {
246                                            lastName = getFirstValue(
247                                                    fetchResp.getAttributeValues("lastName"));
248                                    }
249    
250                                    if (Validator.isNull(emailAddress)) {
251                                            emailAddress = getFirstValue(
252                                                    fetchResp.getAttributeValues("email"));
253                                    }
254                            }
255                    }
256    
257                    String openId = OpenIdUtil.normalize(authSuccess.getIdentity());
258    
259                    User user = null;
260    
261                    try {
262                            user = UserLocalServiceUtil.getUserByOpenId(
263                                    themeDisplay.getCompanyId(), openId);
264                    }
265                    catch (NoSuchUserException nsue) {
266                            if (Validator.isNull(firstName) || Validator.isNull(lastName) ||
267                                    Validator.isNull(emailAddress)) {
268    
269                                    SessionMessages.add(request, "missingOpenIdUserInformation");
270    
271                                    if (_log.isInfoEnabled()) {
272                                            _log.info(
273                                                    "The OpenID provider did not send the required " +
274                                                            "attributes to create an account");
275                                    }
276    
277                                    String createAccountURL = PortalUtil.getCreateAccountURL(
278                                            request, themeDisplay);
279    
280                                    createAccountURL = HttpUtil.setParameter(
281                                            createAccountURL, "openId", openId);
282    
283                                    session.setAttribute(
284                                            WebKeys.OPEN_ID_LOGIN_PENDING, Boolean.TRUE);
285    
286                                    return createAccountURL;
287                            }
288    
289                            long creatorUserId = 0;
290                            long companyId = themeDisplay.getCompanyId();
291                            boolean autoPassword = false;
292                            String password1 = PwdGenerator.getPassword();
293                            String password2 = password1;
294                            boolean autoScreenName = true;
295                            String screenName = StringPool.BLANK;
296                            long facebookId = 0;
297                            Locale locale = themeDisplay.getLocale();
298                            String middleName = StringPool.BLANK;
299                            int prefixId = 0;
300                            int suffixId = 0;
301                            boolean male = true;
302                            int birthdayMonth = Calendar.JANUARY;
303                            int birthdayDay = 1;
304                            int birthdayYear = 1970;
305                            String jobTitle = StringPool.BLANK;
306                            long[] groupIds = null;
307                            long[] organizationIds = null;
308                            long[] roleIds = null;
309                            long[] userGroupIds = null;
310                            boolean sendEmail = false;
311    
312                            ServiceContext serviceContext = new ServiceContext();
313    
314                            user = UserLocalServiceUtil.addUser(
315                                    creatorUserId, companyId, autoPassword, password1, password2,
316                                    autoScreenName, screenName, emailAddress, facebookId, openId,
317                                    locale, firstName, middleName, lastName, prefixId, suffixId,
318                                    male, birthdayMonth, birthdayDay, birthdayYear, jobTitle,
319                                    groupIds, organizationIds, roleIds, userGroupIds, sendEmail,
320                                    serviceContext);
321                    }
322    
323                    session.setAttribute(WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
324    
325                    return null;
326            }
327    
328            protected void sendOpenIdRequest(
329                            ThemeDisplay themeDisplay, ActionRequest actionRequest,
330                            ActionResponse actionResponse)
331                    throws Exception {
332    
333                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
334                            actionRequest);
335                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
336                            actionResponse);
337                    HttpSession session = request.getSession();
338    
339                    ActionResponseImpl actionResponseImpl =
340                            (ActionResponseImpl)actionResponse;
341    
342                    String openId = ParamUtil.getString(actionRequest, "openId");
343    
344                    PortletURL portletURL = actionResponseImpl.createActionURL();
345    
346                    portletURL.setParameter("struts_action", "/login/open_id");
347                    portletURL.setParameter(Constants.CMD, Constants.READ);
348                    portletURL.setParameter("saveLastPath", "0");
349    
350                    ConsumerManager manager = OpenIdUtil.getConsumerManager();
351    
352                    List<DiscoveryInformation> discoveries = manager.discover(openId);
353    
354                    DiscoveryInformation discovered = manager.associate(discoveries);
355    
356                    session.setAttribute(WebKeys.OPEN_ID_DISCO, discovered);
357    
358                    AuthRequest authRequest = manager.authenticate(
359                            discovered, portletURL.toString(), themeDisplay.getPortalURL());
360    
361                    try {
362                            UserLocalServiceUtil.getUserByOpenId(
363                                    themeDisplay.getCompanyId(), openId);
364                    }
365                    catch (NoSuchUserException nsue) {
366                            String screenName = OpenIdUtil.getScreenName(openId);
367    
368                            try {
369                                    User user = UserLocalServiceUtil.getUserByScreenName(
370                                            themeDisplay.getCompanyId(), screenName);
371    
372                                    UserLocalServiceUtil.updateOpenId(user.getUserId(), openId);
373                            }
374                            catch (NoSuchUserException nsue2) {
375                                    FetchRequest fetch = FetchRequest.createFetchRequest();
376    
377                                    fetch.addAttribute(
378                                            "email", "http://schema.openid.net/contact/email", true);
379                                    fetch.addAttribute(
380                                            "firstName", "http://schema.openid.net/namePerson/first",
381                                            true);
382                                    fetch.addAttribute(
383                                            "lastName", "http://schema.openid.net/namePerson/last",
384                                            true);
385    
386                                    authRequest.addExtension(fetch);
387    
388                                    SRegRequest sregRequest = SRegRequest.createFetchRequest();
389    
390                                    sregRequest.addAttribute("fullname", true);
391                                    sregRequest.addAttribute("email", true);
392    
393                                    authRequest.addExtension(sregRequest);
394                            }
395                    }
396    
397                    response.sendRedirect(authRequest.getDestinationUrl(true));
398            }
399    
400            private static final boolean _CHECK_METHOD_ON_PROCESS_ACTION = false;
401    
402            private static Log _log = LogFactoryUtil.getLog(OpenIdAction.class);
403    
404    }