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