/** * 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.portal.security.sso.google.internal; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest; import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse; import com.google.api.client.http.HttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.services.oauth2.Oauth2; import com.google.api.services.oauth2.model.Userinfoplus; import com.liferay.portal.kernel.exception.SystemException; 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.module.configuration.ConfigurationException; import com.liferay.portal.kernel.module.configuration.ConfigurationProvider; 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.settings.CompanyServiceSettingsLocator; import com.liferay.portal.kernel.util.CalendarFactoryUtil; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.ServiceBeanMethodInvocationFactoryUtil; 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.google.GoogleAuthorization; import com.liferay.portal.security.sso.google.configuration.GoogleAuthorizationConfiguration; import com.liferay.portal.security.sso.google.constants.GoogleConstants; import com.liferay.portal.security.sso.google.constants.GoogleWebKeys; import java.lang.reflect.Method; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.Objects; import javax.servlet.http.HttpSession; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * Serves as the core implementation of the Google protocol. * * @author Stian Sigvartsen */ @Component( configurationPid = "com.liferay.portal.security.sso.google.configuration.GoogleAuthorizationConfiguration", immediate = true, service = GoogleAuthorization.class ) public class GoogleAuthorizationImpl implements GoogleAuthorization { public GoogleAuthorizationImpl() { try { Class<?> clazz = getClass(); _doAddOrUpdateUser = clazz.getDeclaredMethod( "doAddOrUpdateUser", HttpSession.class, long.class, Userinfoplus.class); } catch (Exception e) { throw new SystemException(e); } } @Override public User addOrUpdateUser( HttpSession session, long companyId, String authorizationCode, String returnRequestUri, List<String> scopes) throws Exception { GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow = getGoogleAuthorizationCodeFlow(companyId, scopes); GoogleAuthorizationCodeTokenRequest googleAuthorizationCodeTokenRequest = googleAuthorizationCodeFlow.newTokenRequest(authorizationCode); googleAuthorizationCodeTokenRequest.setRedirectUri(returnRequestUri); GoogleTokenResponse googleTokenResponse = googleAuthorizationCodeTokenRequest.execute(); Credential credential = googleAuthorizationCodeFlow.createAndStoreCredential( googleTokenResponse, null); Userinfoplus userinfoplus = getUserinfoplus(credential); if (userinfoplus == null) { return null; } ServiceBeanMethodInvocationFactoryUtil.proceed( this, GoogleAuthorizationImpl.class, _doAddOrUpdateUser, new Object[] {session, companyId, userinfoplus}, new String[] {"transactionAdvice"}); return doAddOrUpdateUser(session, companyId, userinfoplus); } @Override public String getLoginRedirect( long companyId, String returnRequestUri, List<String> scopes) throws Exception { GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow = getGoogleAuthorizationCodeFlow(companyId, scopes); GoogleAuthorizationCodeRequestUrl googleAuthorizationCodeRequestUrl = googleAuthorizationCodeFlow.newAuthorizationUrl(); googleAuthorizationCodeRequestUrl = googleAuthorizationCodeRequestUrl.setRedirectUri(returnRequestUri); return googleAuthorizationCodeRequestUrl.build(); } @Override public boolean isEnabled(long companyId) { GoogleAuthorizationConfiguration googleConfiguration = getGoogleConfiguration(companyId); if (Validator.isNull(googleConfiguration.clientId()) || Validator.isNull(googleConfiguration.clientSecret())) { return false; } return googleConfiguration.enabled(); } protected User addUser(long companyId, Userinfoplus userinfoplus) 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 = userinfoplus.getEmail(); String googleUserId = userinfoplus.getId(); String openId = StringPool.BLANK; Locale locale = LocaleUtil.getDefault(); String firstName = userinfoplus.getGivenName(); String middleName = StringPool.BLANK; String lastName = userinfoplus.getFamilyName(); long prefixId = 0; long suffixId = 0; boolean male = Objects.equals(userinfoplus.getGender(), "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, 0, openId, locale, firstName, middleName, lastName, prefixId, suffixId, male, birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds, organizationIds, roleIds, userGroupIds, sendEmail, serviceContext); user = _userLocalService.updateGoogleUserId( user.getUserId(), googleUserId); user = _userLocalService.updateLastLogin( user.getUserId(), user.getLoginIP()); user = _userLocalService.updatePasswordReset(user.getUserId(), false); user = _userLocalService.updateEmailAddressVerified( user.getUserId(), true); return user; } protected User doAddOrUpdateUser( HttpSession session, long companyId, Userinfoplus userinfoplus) throws Exception { User user = null; String googleUserId = userinfoplus.getId(); if (Validator.isNotNull(googleUserId)) { user = _userLocalService.fetchUserByGoogleUserId( companyId, googleUserId); if ((user != null) && (user.getStatus() != WorkflowConstants.STATUS_INCOMPLETE)) { session.setAttribute( GoogleWebKeys.GOOGLE_USER_ID, String.valueOf(googleUserId)); } } String emailAddress = userinfoplus.getEmail(); if ((user == null) && Validator.isNotNull(emailAddress)) { user = _userLocalService.fetchUserByEmailAddress( companyId, emailAddress); if ((user != null) && (user.getStatus() != WorkflowConstants.STATUS_INCOMPLETE)) { session.setAttribute( GoogleWebKeys.GOOGLE_USER_EMAIL_ADDRESS, emailAddress); } } if (user != null) { if (user.getStatus() == WorkflowConstants.STATUS_INCOMPLETE) { session.setAttribute( WebKeys.GOOGLE_INCOMPLETE_USER_ID, userinfoplus.getId()); user.setEmailAddress(userinfoplus.getEmail()); user.setFirstName(userinfoplus.getGivenName()); user.setLastName(userinfoplus.getFamilyName()); return user; } user = updateUser(user, userinfoplus); } else { user = addUser(companyId, userinfoplus); session.setAttribute( GoogleWebKeys.GOOGLE_USER_EMAIL_ADDRESS, emailAddress); } return user; } protected GoogleAuthorizationCodeFlow getGoogleAuthorizationCodeFlow( long companyId, List<String> scopes) throws Exception { GoogleAuthorizationConfiguration googleAuthorizationConfiguration = getGoogleConfiguration(companyId); HttpTransport httpTransport = new NetHttpTransport(); JacksonFactory jsonFactory = new JacksonFactory(); GoogleAuthorizationCodeFlow.Builder googleAuthorizationCodeFlowBuilder = new GoogleAuthorizationCodeFlow.Builder( httpTransport, jsonFactory, googleAuthorizationConfiguration.clientId(), googleAuthorizationConfiguration.clientSecret(), scopes); googleAuthorizationCodeFlowBuilder = googleAuthorizationCodeFlowBuilder.setAccessType( _ONLINE_ACCESS_TYPE); return googleAuthorizationCodeFlowBuilder.build(); } protected GoogleAuthorizationConfiguration getGoogleConfiguration( long companyId) { try { return _configurationProvider.getConfiguration( GoogleAuthorizationConfiguration.class, new CompanyServiceSettingsLocator( companyId, GoogleConstants.SERVICE_NAME)); } catch (ConfigurationException ce) { throw new SystemException(ce); } } protected Userinfoplus getUserinfoplus(Credential credentials) throws Exception { Oauth2.Builder builder = new Oauth2.Builder( new NetHttpTransport(), new JacksonFactory(), credentials); Oauth2 oauth2 = builder.build(); Oauth2.Userinfo oAuth2Userinfo = oauth2.userinfo(); Oauth2.Userinfo.Get oAuth2UserinfoGet = oAuth2Userinfo.get(); Userinfoplus userinfoplus = oAuth2UserinfoGet.execute(); if ((userinfoplus == null) || (userinfoplus.getId() == null)) { throw new PrincipalException(); } return userinfoplus; } protected User updateUser(User user, Userinfoplus userinfoplus) throws Exception { String emailAddress = userinfoplus.getEmail(); String googleUserId = userinfoplus.getId(); String firstName = userinfoplus.getGivenName(); String lastName = userinfoplus.getFamilyName(); boolean male = Objects.equals(userinfoplus.getGender(), "male"); if (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( googleUserId, user.getGoogleUserId())) { _userLocalService.updateGoogleUserId( user.getUserId(), googleUserId); } 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, 0, 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 static final String _ONLINE_ACCESS_TYPE = "online"; @Reference private ConfigurationProvider _configurationProvider; private final Method _doAddOrUpdateUser; @Reference private UserLocalService _userLocalService; }