001    /**
002     * Copyright (c) 2000-2013 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.kernel.configuration.Filter;
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.PropsKeys;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.Validator;
031    import com.liferay.portal.model.User;
032    import com.liferay.portal.security.auth.PrincipalException;
033    import com.liferay.portal.service.ServiceContext;
034    import com.liferay.portal.service.UserLocalServiceUtil;
035    import com.liferay.portal.struts.PortletAction;
036    import com.liferay.portal.theme.ThemeDisplay;
037    import com.liferay.portal.util.OpenIdUtil;
038    import com.liferay.portal.util.PortalUtil;
039    import com.liferay.portal.util.PropsUtil;
040    import com.liferay.portal.util.PropsValues;
041    import com.liferay.portal.util.WebKeys;
042    import com.liferay.portlet.ActionResponseImpl;
043    import com.liferay.util.PwdGenerator;
044    
045    import java.net.URL;
046    
047    import java.util.Calendar;
048    import java.util.List;
049    import java.util.Locale;
050    
051    import javax.portlet.ActionRequest;
052    import javax.portlet.ActionResponse;
053    import javax.portlet.PortletConfig;
054    import javax.portlet.PortletURL;
055    import javax.portlet.RenderRequest;
056    import javax.portlet.RenderResponse;
057    
058    import javax.servlet.http.HttpServletRequest;
059    import javax.servlet.http.HttpServletResponse;
060    import javax.servlet.http.HttpSession;
061    
062    import org.apache.struts.action.ActionForm;
063    import org.apache.struts.action.ActionForward;
064    import org.apache.struts.action.ActionMapping;
065    
066    import org.openid4java.OpenIDException;
067    import org.openid4java.consumer.ConsumerManager;
068    import org.openid4java.consumer.VerificationResult;
069    import org.openid4java.discovery.DiscoveryInformation;
070    import org.openid4java.discovery.Identifier;
071    import org.openid4java.message.AuthRequest;
072    import org.openid4java.message.AuthSuccess;
073    import org.openid4java.message.MessageExtension;
074    import org.openid4java.message.ParameterList;
075    import org.openid4java.message.ax.AxMessage;
076    import org.openid4java.message.ax.FetchRequest;
077    import org.openid4java.message.ax.FetchResponse;
078    import org.openid4java.message.sreg.SRegMessage;
079    import org.openid4java.message.sreg.SRegRequest;
080    import org.openid4java.message.sreg.SRegResponse;
081    
082    /**
083     * @author Brian Wing Shun Chan
084     * @author Jorge Ferrer
085     */
086    public class OpenIdAction extends PortletAction {
087    
088            @Override
089            public void processAction(
090                            ActionMapping actionMapping, ActionForm actionForm,
091                            PortletConfig portletConfig, ActionRequest actionRequest,
092                            ActionResponse actionResponse)
093                    throws Exception {
094    
095                    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
096                            WebKeys.THEME_DISPLAY);
097    
098                    if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
099                            throw new PrincipalException();
100                    }
101    
102                    if (actionRequest.getRemoteUser() != null) {
103                            actionResponse.sendRedirect(themeDisplay.getPathMain());
104    
105                            return;
106                    }
107    
108                    String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
109    
110                    try {
111                            if (cmd.equals(Constants.READ)) {
112                                    String redirect = readOpenIdResponse(
113                                            themeDisplay, actionRequest, actionResponse);
114    
115                                    if (Validator.isNull(redirect)) {
116                                            redirect = 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());
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());
137                            }
138                            else {
139                                    _log.error("Error processing the OpenID login", e);
140    
141                                    PortalUtil.sendError(e, actionRequest, actionResponse);
142                            }
143                    }
144            }
145    
146            @Override
147            public ActionForward render(
148                            ActionMapping actionMapping, ActionForm actionForm,
149                            PortletConfig portletConfig, RenderRequest renderRequest,
150                            RenderResponse renderResponse)
151                    throws Exception {
152    
153                    ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
154                            WebKeys.THEME_DISPLAY);
155    
156                    if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
157                            return actionMapping.findForward("portlet.login.login");
158                    }
159    
160                    renderResponse.setTitle(themeDisplay.translate("open-id"));
161    
162                    return actionMapping.findForward("portlet.login.open_id");
163            }
164    
165            protected String getFirstValue(List<String> values) {
166                    if ((values == null) || (values.size() < 1)) {
167                            return null;
168                    }
169    
170                    return values.get(0);
171            }
172    
173            protected String getOpenIdProvider(URL endpointURL) {
174                    String hostName = endpointURL.getHost();
175    
176                    for (String openIdProvider : PropsValues.OPEN_ID_PROVIDERS) {
177                            String openIdURLString = PropsUtil.get(
178                                    PropsKeys.OPEN_ID_URL, new Filter(openIdProvider));
179    
180                            if (hostName.equals(openIdURLString)) {
181                                    return openIdProvider;
182                            }
183                    }
184    
185                    return _OPEN_ID_PROVIDER_DEFAULT;
186            }
187    
188            @Override
189            protected boolean isCheckMethodOnProcessAction() {
190                    return _CHECK_METHOD_ON_PROCESS_ACTION;
191            }
192    
193            protected String readOpenIdResponse(
194                            ThemeDisplay themeDisplay, ActionRequest actionRequest,
195                            ActionResponse actionResponse)
196                    throws Exception {
197    
198                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
199                            actionRequest);
200    
201                    request = PortalUtil.getOriginalServletRequest(request);
202    
203                    HttpSession session = request.getSession();
204    
205                    ConsumerManager consumerManager = OpenIdUtil.getConsumerManager();
206    
207                    ParameterList parameterList = new ParameterList(
208                            request.getParameterMap());
209    
210                    DiscoveryInformation discoveryInformation =
211                            (DiscoveryInformation)session.getAttribute(WebKeys.OPEN_ID_DISCO);
212    
213                    if (discoveryInformation == null) {
214                            return null;
215                    }
216    
217                    String receivingURL = ParamUtil.getString(request, "openid.return_to");
218    
219                    VerificationResult verificationResult = consumerManager.verify(
220                            receivingURL, parameterList, discoveryInformation);
221    
222                    Identifier identifier = verificationResult.getVerifiedId();
223    
224                    if (identifier == null) {
225                            return null;
226                    }
227    
228                    AuthSuccess authSuccess =
229                            (AuthSuccess)verificationResult.getAuthResponse();
230    
231                    String firstName = null;
232                    String lastName = null;
233                    String emailAddress = null;
234    
235                    if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) {
236                            MessageExtension messageExtension = authSuccess.getExtension(
237                                    SRegMessage.OPENID_NS_SREG);
238    
239                            if (messageExtension instanceof SRegResponse) {
240                                    SRegResponse sregResp = (SRegResponse)messageExtension;
241    
242                                    String fullName = GetterUtil.getString(
243                                            sregResp.getAttributeValue(_OPEN_ID_SREG_ATTR_FULLNAME));
244    
245                                    String[] names = splitFullName(fullName);
246    
247                                    if (names != null) {
248                                            firstName = names[0];
249                                            lastName = names[1];
250                                    }
251    
252                                    emailAddress = sregResp.getAttributeValue(
253                                            _OPEN_ID_SREG_ATTR_EMAIL);
254                            }
255                    }
256    
257                    if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
258                            MessageExtension messageExtension = authSuccess.getExtension(
259                                    AxMessage.OPENID_NS_AX);
260    
261                            if (messageExtension instanceof FetchResponse) {
262                                    FetchResponse fetchResponse = (FetchResponse)messageExtension;
263    
264                                    String openIdProvider = getOpenIdProvider(
265                                            discoveryInformation.getOPEndpoint());
266    
267                                    String[] openIdAXTypes = PropsUtil.getArray(
268                                            PropsKeys.OPEN_ID_AX_SCHEMA, new Filter(openIdProvider));
269    
270                                    for (String openIdAXType : openIdAXTypes) {
271                                            if (openIdAXType.equals(_OPEN_ID_AX_ATTR_EMAIL)) {
272                                                    if (Validator.isNull(emailAddress)) {
273                                                            emailAddress = getFirstValue(
274                                                                    fetchResponse.getAttributeValues(
275                                                                            _OPEN_ID_AX_ATTR_EMAIL));
276                                                    }
277                                            }
278                                            else if (openIdAXType.equals(_OPEN_ID_AX_ATTR_FIRST_NAME)) {
279                                                    if (Validator.isNull(firstName)) {
280                                                            firstName = getFirstValue(
281                                                                    fetchResponse.getAttributeValues(
282                                                                            _OPEN_ID_AX_ATTR_FIRST_NAME));
283                                                    }
284                                            }
285                                            else if (openIdAXType.equals(_OPEN_ID_AX_ATTR_FULL_NAME)) {
286                                                    String fullName = fetchResponse.getAttributeValue(
287                                                            _OPEN_ID_AX_ATTR_FULL_NAME);
288    
289                                                    String[] names = splitFullName(fullName);
290    
291                                                    if (names != null) {
292                                                            if (Validator.isNull(firstName)) {
293                                                                    firstName = names[0];
294                                                            }
295    
296                                                            if (Validator.isNull(lastName)) {
297                                                                    lastName = names[1];
298                                                            }
299                                                    }
300                                            }
301                                            else if (openIdAXType.equals(_OPEN_ID_AX_ATTR_LAST_NAME)) {
302                                                    if (Validator.isNull(lastName)) {
303                                                            lastName = getFirstValue(
304                                                                    fetchResponse.getAttributeValues(
305                                                                            _OPEN_ID_AX_ATTR_LAST_NAME));
306                                                    }
307                                            }
308                                    }
309                            }
310                    }
311    
312                    String openId = OpenIdUtil.normalize(authSuccess.getIdentity());
313    
314                    User user = UserLocalServiceUtil.fetchUserByOpenId(
315                            themeDisplay.getCompanyId(), openId);
316    
317                    if (user != null) {
318                            session.setAttribute(
319                                    WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
320    
321                            return null;
322                    }
323    
324                    if (Validator.isNull(firstName) || Validator.isNull(lastName) ||
325                            Validator.isNull(emailAddress)) {
326    
327                            SessionMessages.add(request, "openIdUserInformationMissing");
328    
329                            if (_log.isInfoEnabled()) {
330                                    _log.info(
331                                            "The OpenID provider did not send the required " +
332                                                    "attributes to create an account");
333                            }
334    
335                            String createAccountURL = PortalUtil.getCreateAccountURL(
336                                    request, themeDisplay);
337    
338                            createAccountURL = HttpUtil.setParameter(
339                                    createAccountURL, "openId", openId);
340    
341                            session.setAttribute(WebKeys.OPEN_ID_LOGIN_PENDING, Boolean.TRUE);
342    
343                            return createAccountURL;
344                    }
345    
346                    long creatorUserId = 0;
347                    long companyId = themeDisplay.getCompanyId();
348                    boolean autoPassword = false;
349                    String password1 = PwdGenerator.getPassword();
350                    String password2 = password1;
351                    boolean autoScreenName = true;
352                    String screenName = StringPool.BLANK;
353                    long facebookId = 0;
354                    Locale locale = themeDisplay.getLocale();
355                    String middleName = StringPool.BLANK;
356                    int prefixId = 0;
357                    int suffixId = 0;
358                    boolean male = true;
359                    int birthdayMonth = Calendar.JANUARY;
360                    int birthdayDay = 1;
361                    int birthdayYear = 1970;
362                    String jobTitle = StringPool.BLANK;
363                    long[] groupIds = null;
364                    long[] organizationIds = null;
365                    long[] roleIds = null;
366                    long[] userGroupIds = null;
367                    boolean sendEmail = false;
368    
369                    ServiceContext serviceContext = new ServiceContext();
370    
371                    user = UserLocalServiceUtil.addUser(
372                            creatorUserId, companyId, autoPassword, password1, password2,
373                            autoScreenName, screenName, emailAddress, facebookId, openId,
374                            locale, firstName, middleName, lastName, prefixId, suffixId, male,
375                            birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds,
376                            organizationIds, roleIds, userGroupIds, sendEmail, serviceContext);
377    
378                    session.setAttribute(WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
379    
380                    return null;
381            }
382    
383            protected void sendOpenIdRequest(
384                            ThemeDisplay themeDisplay, ActionRequest actionRequest,
385                            ActionResponse actionResponse)
386                    throws Exception {
387    
388                    HttpServletRequest request = PortalUtil.getHttpServletRequest(
389                            actionRequest);
390                    HttpServletResponse response = PortalUtil.getHttpServletResponse(
391                            actionResponse);
392                    HttpSession session = request.getSession();
393    
394                    ActionResponseImpl actionResponseImpl =
395                            (ActionResponseImpl)actionResponse;
396    
397                    String openId = ParamUtil.getString(actionRequest, "openId");
398    
399                    PortletURL portletURL = actionResponseImpl.createActionURL();
400    
401                    portletURL.setParameter("saveLastPath", Boolean.FALSE.toString());
402                    portletURL.setParameter(Constants.CMD, Constants.READ);
403                    portletURL.setParameter("struts_action", "/login/open_id");
404    
405                    ConsumerManager manager = OpenIdUtil.getConsumerManager();
406    
407                    List<DiscoveryInformation> discoveries = manager.discover(openId);
408    
409                    DiscoveryInformation discovered = manager.associate(discoveries);
410    
411                    session.setAttribute(WebKeys.OPEN_ID_DISCO, discovered);
412    
413                    AuthRequest authRequest = manager.authenticate(
414                            discovered, portletURL.toString(), themeDisplay.getPortalURL());
415    
416                    if (UserLocalServiceUtil.fetchUserByOpenId(
417                                    themeDisplay.getCompanyId(), openId) != null) {
418    
419                            response.sendRedirect(authRequest.getDestinationUrl(true));
420    
421                            return;
422                    }
423    
424                    String screenName = OpenIdUtil.getScreenName(openId);
425    
426                    User user = UserLocalServiceUtil.fetchUserByScreenName(
427                            themeDisplay.getCompanyId(), screenName);
428    
429                    if (user != null) {
430                            UserLocalServiceUtil.updateOpenId(user.getUserId(), openId);
431    
432                            response.sendRedirect(authRequest.getDestinationUrl(true));
433    
434                            return;
435                    }
436    
437                    FetchRequest fetchRequest = FetchRequest.createFetchRequest();
438    
439                    String openIdProvider = getOpenIdProvider(discovered.getOPEndpoint());
440    
441                    String[] openIdAXTypes = PropsUtil.getArray(
442                            PropsKeys.OPEN_ID_AX_SCHEMA, new Filter(openIdProvider));
443    
444                    for (String openIdAXType : openIdAXTypes) {
445                            fetchRequest.addAttribute(
446                                    openIdAXType,
447                                    PropsUtil.get(
448                                            _OPEN_ID_AX_TYPE.concat(openIdAXType),
449                                            new Filter(openIdProvider)), true);
450                    }
451    
452                    authRequest.addExtension(fetchRequest);
453    
454                    SRegRequest sRegRequest = SRegRequest.createFetchRequest();
455    
456                    sRegRequest.addAttribute(_OPEN_ID_SREG_ATTR_EMAIL, true);
457                    sRegRequest.addAttribute(_OPEN_ID_SREG_ATTR_FULLNAME, true);
458    
459                    authRequest.addExtension(sRegRequest);
460    
461                    response.sendRedirect(authRequest.getDestinationUrl(true));
462            }
463    
464            protected String[] splitFullName(String fullName) {
465                    if (Validator.isNull(fullName)) {
466                            return null;
467                    }
468    
469                    int pos = fullName.indexOf(CharPool.SPACE);
470    
471                    if ((pos != -1) && ((pos + 1) < fullName.length())) {
472                            String[] names = new String[2];
473    
474                            names[0] = fullName.substring(0, pos);
475                            names[1] = fullName.substring(pos + 1);
476    
477                            return names;
478                    }
479    
480                    return null;
481            }
482    
483            private static final boolean _CHECK_METHOD_ON_PROCESS_ACTION = false;
484    
485            private static final String _OPEN_ID_AX_ATTR_EMAIL = "email";
486    
487            private static final String _OPEN_ID_AX_ATTR_FIRST_NAME = "firstname";
488    
489            private static final String _OPEN_ID_AX_ATTR_FULL_NAME = "fullname";
490    
491            private static final String _OPEN_ID_AX_ATTR_LAST_NAME = "lastname";
492    
493            private static final String _OPEN_ID_AX_TYPE = "open.id.ax.type.";
494    
495            private static final String _OPEN_ID_PROVIDER_DEFAULT = "default";
496    
497            private static final String _OPEN_ID_SREG_ATTR_EMAIL = "email";
498    
499            private static final String _OPEN_ID_SREG_ATTR_FULLNAME = "fullname";
500    
501            private static Log _log = LogFactoryUtil.getLog(OpenIdAction.class);
502    
503    }