/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.login.authentication.facebook.connect.web.internal.portlet.action; import com.liferay.portal.kernel.facebook.FacebookConnect; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.model.Contact; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.model.UserGroupRole; import com.liferay.portal.kernel.portlet.LiferayWindowState; import com.liferay.portal.kernel.portlet.PortletURLFactoryUtil; import com.liferay.portal.kernel.security.auth.PrincipalException; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.UserLocalService; import com.liferay.portal.kernel.struts.BaseStrutsAction; import com.liferay.portal.kernel.struts.StrutsAction; import com.liferay.portal.kernel.theme.ThemeDisplay; import com.liferay.portal.kernel.util.CalendarFactoryUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.ParamUtil; import com.liferay.portal.kernel.util.Portal; import com.liferay.portal.kernel.util.PortletKeys; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.kernel.workflow.WorkflowConstants; import com.liferay.portal.security.sso.facebook.connect.constants.FacebookConnectWebKeys; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import javax.portlet.PortletMode; import javax.portlet.PortletRequest; import javax.portlet.PortletURL; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * Receives HTTP servlet requests much like a servlet. * * <p> * By setting the <code>path</code> property on the <code>@Component</code> * annotation to <code>/login/facebook_connect_oauth/</code>, this Struts action * is published at the URL <code>/c/login/facebook_connect_oauth/</code>, which * corresponds to the default OAuth redirect URL. This Struts action carries out * these tasks: * </p> * * <ol> * <li> * Exchanges OAuth request tokens for OAuth access tokens. * </li> * <li> * Retrieves the current user's Facebook ID and email address using the * Facebook Graph API. * </li> * <li> * If either the Facebook ID or email address matches an existing Liferay * user, then one of two HTTP session attributes is set: * <code>FACEBOOK_USER_ID</code> or <code>FACEBOOK_USER_EMAIL_ADDRESS</code>. * </li> * <li> * If no matching Liferay user is found, a new Liferay Portal user is * created and the HTTP session attribute * <code>FACEBOOK_USER_EMAIL_ADDRESS</code> is set accordingly. If the data * available from Facebook is insufficient to successfully create a new Liferay * user, the user is directed to submit the missing information to complete the * process. * </li> * </ol> * * @author Wilson Man * @author Sergio González * @author Mika Koivisto */ @Component( immediate = true, property = { "/common/referer_jsp.jsp=/common/referer_jsp.jsp", "path=/portal/facebook_connect_oauth", "portlet.login.login=portlet.login.login", "portlet.login.update_account=portlet.login.update_account" }, service = StrutsAction.class ) public class FacebookConnectAction extends BaseStrutsAction { @Override public String execute( HttpServletRequest request, HttpServletResponse response) throws Exception { ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute( WebKeys.THEME_DISPLAY); if (!_facebookConnect.isEnabled(themeDisplay.getCompanyId())) { throw new PrincipalException.MustBeEnabled( themeDisplay.getCompanyId(), FacebookConnect.class.getName()); } HttpSession session = request.getSession(); String redirect = ParamUtil.getString(request, "redirect"); redirect = _portal.escapeRedirect(redirect); String code = ParamUtil.getString(request, "code"); String token = _facebookConnect.getAccessToken( themeDisplay.getCompanyId(), redirect, code); if (Validator.isNotNull(token)) { User user = setFacebookCredentials( session, themeDisplay.getCompanyId(), token); if ((user != null) && (user.getStatus() == WorkflowConstants.STATUS_INCOMPLETE)) { redirectUpdateAccount(request, response, user); return null; } } else { return _forwards.get("/common/referer_jsp.jsp"); } response.sendRedirect(redirect); return null; } @Activate protected void activate(Map<String, Object> properties) { _forwards.put( "/common/referer_jsp.jsp", GetterUtil.getString(properties, "/common/referer_jsp.jsp")); _forwards.put( "portlet.login.login", GetterUtil.getString(properties, "portlet.login.login")); _forwards.put( "portlet.login.update_account", GetterUtil.getString(properties, "portlet.login.update_account")); } protected User addUser( HttpSession session, long companyId, JSONObject jsonObject) throws Exception { long creatorUserId = 0; boolean autoPassword = true; String password1 = StringPool.BLANK; String password2 = StringPool.BLANK; boolean autoScreenName = true; String screenName = StringPool.BLANK; String emailAddress = jsonObject.getString("email"); long facebookId = jsonObject.getLong("id"); String openId = StringPool.BLANK; Locale locale = LocaleUtil.getDefault(); String firstName = jsonObject.getString("first_name"); String middleName = StringPool.BLANK; String lastName = jsonObject.getString("last_name"); long prefixId = 0; long suffixId = 0; boolean male = Objects.equals(jsonObject.getString("gender"), "male"); int birthdayMonth = Calendar.JANUARY; int birthdayDay = 1; int birthdayYear = 1970; String jobTitle = StringPool.BLANK; long[] groupIds = null; long[] organizationIds = null; long[] roleIds = null; long[] userGroupIds = null; boolean sendEmail = true; ServiceContext serviceContext = new ServiceContext(); User user = _userLocalService.addUser( creatorUserId, companyId, autoPassword, password1, password2, autoScreenName, screenName, emailAddress, facebookId, openId, locale, firstName, middleName, lastName, prefixId, suffixId, male, birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds, organizationIds, roleIds, userGroupIds, sendEmail, serviceContext); user = _userLocalService.updateLastLogin( user.getUserId(), user.getLoginIP()); user = _userLocalService.updatePasswordReset(user.getUserId(), false); user = _userLocalService.updateEmailAddressVerified( user.getUserId(), true); session.setAttribute(WebKeys.FACEBOOK_USER_EMAIL_ADDRESS, emailAddress); return user; } protected void redirectUpdateAccount( HttpServletRequest request, HttpServletResponse response, User user) throws Exception { PortletURL portletURL = PortletURLFactoryUtil.create( request, PortletKeys.LOGIN, PortletRequest.RENDER_PHASE); portletURL.setParameter("saveLastPath", Boolean.FALSE.toString()); portletURL.setParameter( "mvcRenderCommandName", "/login/associate_facebook_user"); portletURL.setParameter( "redirect", ParamUtil.getString(request, "redirect")); portletURL.setParameter("userId", String.valueOf(user.getUserId())); portletURL.setParameter("emailAddress", user.getEmailAddress()); portletURL.setParameter("firstName", user.getFirstName()); portletURL.setParameter("lastName", user.getLastName()); portletURL.setPortletMode(PortletMode.VIEW); portletURL.setWindowState(LiferayWindowState.POP_UP); response.sendRedirect(portletURL.toString()); } @Reference(unbind = "-") protected void setFacebookConnect(FacebookConnect facebookConnect) { _facebookConnect = facebookConnect; } protected User setFacebookCredentials( HttpSession session, long companyId, String token) throws Exception { JSONObject jsonObject = _facebookConnect.getGraphResources( companyId, "/me", token, "id,email,first_name,last_name,gender,verified"); if ((jsonObject == null) || (jsonObject.getJSONObject("error") != null)) { return null; } if (_facebookConnect.isVerifiedAccountRequired(companyId) && !jsonObject.getBoolean("verified")) { return null; } User user = null; long facebookId = jsonObject.getLong("id"); if (facebookId > 0) { session.setAttribute( FacebookConnectWebKeys.FACEBOOK_ACCESS_TOKEN, token); user = _userLocalService.fetchUserByFacebookId( companyId, facebookId); if ((user != null) && !user.isActive()) { return null; } else if ((user != null) && (user.getStatus() != WorkflowConstants.STATUS_INCOMPLETE)) { session.setAttribute( FacebookConnectWebKeys.FACEBOOK_USER_ID, String.valueOf(facebookId)); } } String emailAddress = jsonObject.getString("email"); if ((user == null) && Validator.isNotNull(emailAddress)) { user = _userLocalService.fetchUserByEmailAddress( companyId, emailAddress); if ((user != null) && !user.isActive()) { return null; } else if ((user != null) && (user.getStatus() != WorkflowConstants.STATUS_INCOMPLETE)) { session.setAttribute( WebKeys.FACEBOOK_USER_EMAIL_ADDRESS, emailAddress); } } if (user != null) { if (user.getStatus() == WorkflowConstants.STATUS_INCOMPLETE) { session.setAttribute( WebKeys.FACEBOOK_INCOMPLETE_USER_ID, facebookId); user.setEmailAddress(jsonObject.getString("email")); user.setFirstName(jsonObject.getString("first_name")); user.setLastName(jsonObject.getString("last_name")); return user; } user = updateUser(user, jsonObject); } else { user = addUser(session, companyId, jsonObject); } return user; } @Reference(unbind = "-") protected void setUserLocalService(UserLocalService userLocalService) { _userLocalService = userLocalService; } protected User updateUser(User user, JSONObject jsonObject) throws Exception { long facebookId = jsonObject.getLong("id"); String emailAddress = jsonObject.getString("email"); String firstName = jsonObject.getString("first_name"); String lastName = jsonObject.getString("last_name"); boolean male = Objects.equals(jsonObject.getString("gender"), "male"); if ((facebookId == user.getFacebookId()) && emailAddress.equals(user.getEmailAddress()) && firstName.equals(user.getFirstName()) && lastName.equals(user.getLastName()) && (male == user.isMale())) { return user; } Contact contact = user.getContact(); Calendar birthdayCal = CalendarFactoryUtil.getCalendar(); birthdayCal.setTime(contact.getBirthday()); int birthdayMonth = birthdayCal.get(Calendar.MONTH); int birthdayDay = birthdayCal.get(Calendar.DAY_OF_MONTH); int birthdayYear = birthdayCal.get(Calendar.YEAR); long[] groupIds = null; long[] organizationIds = null; long[] roleIds = null; List<UserGroupRole> userGroupRoles = null; long[] userGroupIds = null; ServiceContext serviceContext = new ServiceContext(); if (!StringUtil.equalsIgnoreCase( emailAddress, user.getEmailAddress())) { _userLocalService.updateEmailAddress( user.getUserId(), StringPool.BLANK, emailAddress, emailAddress); } _userLocalService.updateEmailAddressVerified(user.getUserId(), true); return _userLocalService.updateUser( user.getUserId(), StringPool.BLANK, StringPool.BLANK, StringPool.BLANK, false, user.getReminderQueryQuestion(), user.getReminderQueryAnswer(), user.getScreenName(), emailAddress, facebookId, user.getOpenId(), true, null, user.getLanguageId(), user.getTimeZoneId(), user.getGreeting(), user.getComments(), firstName, user.getMiddleName(), lastName, contact.getPrefixId(), contact.getSuffixId(), male, birthdayMonth, birthdayDay, birthdayYear, contact.getSmsSn(), contact.getFacebookSn(), contact.getJabberSn(), contact.getSkypeSn(), contact.getTwitterSn(), contact.getJobTitle(), groupIds, organizationIds, roleIds, userGroupRoles, userGroupIds, serviceContext); } private FacebookConnect _facebookConnect; private final Map<String, String> _forwards = new HashMap<>(); @Reference private Portal _portal; private UserLocalService _userLocalService; }