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