1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.login.action;
24  
25  import com.liferay.portal.DuplicateUserEmailAddressException;
26  import com.liferay.portal.NoSuchUserException;
27  import com.liferay.portal.kernel.log.Log;
28  import com.liferay.portal.kernel.log.LogFactoryUtil;
29  import com.liferay.portal.kernel.servlet.SessionErrors;
30  import com.liferay.portal.kernel.servlet.SessionMessages;
31  import com.liferay.portal.kernel.util.Constants;
32  import com.liferay.portal.kernel.util.GetterUtil;
33  import com.liferay.portal.kernel.util.ParamUtil;
34  import com.liferay.portal.kernel.util.StringPool;
35  import com.liferay.portal.kernel.util.Validator;
36  import com.liferay.portal.model.User;
37  import com.liferay.portal.service.ServiceContext;
38  import com.liferay.portal.service.UserLocalServiceUtil;
39  import com.liferay.portal.struts.PortletAction;
40  import com.liferay.portal.theme.ThemeDisplay;
41  import com.liferay.portal.util.OpenIdUtil;
42  import com.liferay.portal.util.PortalUtil;
43  import com.liferay.portal.util.WebKeys;
44  import com.liferay.portlet.ActionResponseImpl;
45  import com.liferay.util.PwdGenerator;
46  
47  import java.util.Calendar;
48  import java.util.List;
49  import java.util.Locale;
50  
51  import javax.portlet.ActionRequest;
52  import javax.portlet.ActionResponse;
53  import javax.portlet.PortletConfig;
54  import javax.portlet.PortletURL;
55  import javax.portlet.RenderRequest;
56  import javax.portlet.RenderResponse;
57  
58  import javax.servlet.http.HttpServletRequest;
59  import javax.servlet.http.HttpServletResponse;
60  import javax.servlet.http.HttpSession;
61  
62  import org.apache.struts.action.ActionForm;
63  import org.apache.struts.action.ActionForward;
64  import org.apache.struts.action.ActionMapping;
65  
66  import org.openid4java.OpenIDException;
67  import org.openid4java.consumer.ConsumerManager;
68  import org.openid4java.consumer.VerificationResult;
69  import org.openid4java.discovery.DiscoveryInformation;
70  import org.openid4java.discovery.Identifier;
71  import org.openid4java.message.AuthRequest;
72  import org.openid4java.message.AuthSuccess;
73  import org.openid4java.message.MessageExtension;
74  import org.openid4java.message.ParameterList;
75  import org.openid4java.message.ax.AxMessage;
76  import org.openid4java.message.ax.FetchRequest;
77  import org.openid4java.message.ax.FetchResponse;
78  import org.openid4java.message.sreg.SRegMessage;
79  import org.openid4java.message.sreg.SRegRequest;
80  import org.openid4java.message.sreg.SRegResponse;
81  
82  /**
83   * <a href="OpenIdAction.java.html"><b><i>View Source</i></b></a>
84   *
85   * @author Brian Wing Shun Chan
86   * @author Jorge Ferrer
87   */
88  public class OpenIdAction extends PortletAction {
89  
90      public void processAction(
91              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
92              ActionRequest actionRequest, ActionResponse actionResponse)
93          throws Exception {
94  
95          ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
96              WebKeys.THEME_DISPLAY);
97  
98          if (actionRequest.getRemoteUser() != null) {
99              actionResponse.sendRedirect(themeDisplay.getPathMain());
100 
101             return;
102         }
103 
104         String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
105 
106         try {
107             if (cmd.equals(Constants.READ)) {
108                 String redirect = readOpenIdResponse(
109                     themeDisplay, actionRequest, actionResponse);
110 
111                 if (Validator.isNull(redirect)) {
112                     redirect =
113                         PortalUtil.getPortalURL(actionRequest) +
114                             themeDisplay.getURLSignIn();
115                 }
116 
117                 sendRedirect(actionRequest, actionResponse, redirect);
118             }
119             else {
120                 sendOpenIdRequest(themeDisplay, actionRequest, actionResponse);
121             }
122         }
123         catch (Exception e) {
124             if (e instanceof DuplicateUserEmailAddressException) {
125                 SessionErrors.add(actionRequest, e.getClass().getName());
126             }
127             else if (e instanceof OpenIDException) {
128                 if (_log.isInfoEnabled()) {
129                     _log.info(
130                         "Error communicating with OpenID provider: " +
131                             e.getMessage());
132                 }
133 
134                 SessionErrors.add(actionRequest, e.getClass().getName());
135             }
136             else {
137                 _log.error("Error processing the OpenID login", e);
138 
139                 PortalUtil.sendError(e, actionRequest, actionResponse);
140             }
141         }
142     }
143 
144     public ActionForward render(
145             ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
146             RenderRequest renderRequest, RenderResponse renderResponse)
147         throws Exception {
148 
149         ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute(
150             WebKeys.THEME_DISPLAY);
151 
152         renderResponse.setTitle(themeDisplay.translate("open-id"));
153 
154         return mapping.findForward("portlet.login.open_id");
155     }
156 
157     protected String getFirstValue(List<String> values) {
158         if ((values == null) || (values.size() < 1)) {
159             return null;
160         }
161 
162         return values.get(0);
163     }
164 
165     protected boolean isCheckMethodOnProcessAction() {
166         return _CHECK_METHOD_ON_PROCESS_ACTION;
167     }
168 
169     protected String readOpenIdResponse(
170             ThemeDisplay themeDisplay, ActionRequest actionRequest,
171             ActionResponse actionResponse)
172         throws Exception {
173 
174         HttpServletRequest request = PortalUtil.getHttpServletRequest(
175             actionRequest);
176         HttpSession session = request.getSession();
177 
178         ActionResponseImpl actionResponseImpl =
179             (ActionResponseImpl)actionResponse;
180 
181         ConsumerManager manager = OpenIdUtil.getConsumerManager();
182 
183         ParameterList params = new ParameterList(
184             actionRequest.getParameterMap());
185 
186         DiscoveryInformation discovered =
187             (DiscoveryInformation)session.getAttribute(WebKeys.OPEN_ID_DISCO);
188 
189         if (discovered == null) {
190             return null;
191         }
192 
193         PortletURL portletURL = actionResponseImpl.createActionURL();
194 
195         portletURL.setParameter("struts_action", "/login/open_id");
196         portletURL.setParameter(Constants.CMD, Constants.READ);
197         portletURL.setParameter("saveLastPath", "0");
198 
199         VerificationResult verification = manager.verify(
200             portletURL.toString(), params, discovered);
201 
202         Identifier verified = verification.getVerifiedId();
203 
204         if (verified == null) {
205             return null;
206         }
207 
208         AuthSuccess authSuccess = (AuthSuccess)verification.getAuthResponse();
209 
210         String firstName = null;
211         String lastName = null;
212         String emailAddress = null;
213 
214         if (authSuccess.hasExtension(SRegMessage.OPENID_NS_SREG)) {
215             MessageExtension ext = authSuccess.getExtension(
216                 SRegMessage.OPENID_NS_SREG);
217 
218             if (ext instanceof SRegResponse) {
219                 SRegResponse sregResp = (SRegResponse)ext;
220 
221                 String fullName = GetterUtil.getString(
222                     sregResp.getAttributeValue("fullname"));
223 
224                 int pos = fullName.indexOf(StringPool.SPACE);
225 
226                 if ((pos != -1) && ((pos + 1) < fullName.length())) {
227                     firstName = fullName.substring(0, pos);
228                     lastName = fullName.substring(pos + 1);
229                 }
230 
231                 emailAddress = sregResp.getAttributeValue("email");
232             }
233         }
234 
235         if (authSuccess.hasExtension(AxMessage.OPENID_NS_AX)) {
236             MessageExtension ext = authSuccess.getExtension(
237                 AxMessage.OPENID_NS_AX);
238 
239             if (ext instanceof FetchResponse) {
240                 FetchResponse fetchResp = (FetchResponse)ext;
241 
242                 if (Validator.isNull(firstName)) {
243                     firstName = getFirstValue(
244                         fetchResp.getAttributeValues("firstName"));
245                 }
246 
247                 if (Validator.isNull(lastName)) {
248                     lastName = getFirstValue(
249                         fetchResp.getAttributeValues("lastName"));
250                 }
251 
252                 if (Validator.isNull(emailAddress)) {
253                     emailAddress = getFirstValue(
254                         fetchResp.getAttributeValues("email"));
255                 }
256             }
257         }
258 
259         String openId = OpenIdUtil.normalize(authSuccess.getIdentity());
260 
261         User user = null;
262 
263         try {
264             user = UserLocalServiceUtil.getUserByOpenId(openId);
265         }
266         catch (NoSuchUserException nsue) {
267             if (Validator.isNull(firstName) || Validator.isNull(lastName) ||
268                 Validator.isNull(emailAddress)) {
269 
270                 SessionMessages.add(request, "missingOpenIdUserInformation");
271 
272                 if (_log.isInfoEnabled()) {
273                     _log.info(
274                         "The OpenID provider did not send the required " +
275                             "attributes to create an account");
276                 }
277 
278                 PortletURL createAccountURL =
279                     themeDisplay.getURLCreateAccount();
280 
281                 createAccountURL.setParameter("openId", openId);
282 
283                 session.setAttribute(
284                     WebKeys.OPEN_ID_LOGIN_PENDING, Boolean.TRUE);
285 
286                 return createAccountURL.toString();
287             }
288 
289             long creatorUserId = 0;
290             long companyId = themeDisplay.getCompanyId();
291             boolean autoPassword = false;
292             String password1 = PwdGenerator.getPassword();
293             String password2 = password1;
294             boolean autoScreenName = true;
295             String screenName = StringPool.BLANK;
296             Locale locale = themeDisplay.getLocale();
297             String middleName = StringPool.BLANK;
298             int prefixId = 0;
299             int suffixId = 0;
300             boolean male = true;
301             int birthdayMonth = Calendar.JANUARY;
302             int birthdayDay = 1;
303             int birthdayYear = 1970;
304             String jobTitle = StringPool.BLANK;
305             long[] groupIds = null;
306             long[] organizationIds = null;
307             long[] roleIds = null;
308             long[] userGroupIds = null;
309             boolean sendEmail = false;
310 
311             ServiceContext serviceContext = new ServiceContext();
312 
313             user = UserLocalServiceUtil.addUser(
314                 creatorUserId, companyId, autoPassword, password1, password2,
315                 autoScreenName, screenName, emailAddress, openId, locale,
316                 firstName, middleName, lastName, prefixId, suffixId, male,
317                 birthdayMonth, birthdayDay, birthdayYear, jobTitle, groupIds,
318                 organizationIds, roleIds, userGroupIds, sendEmail,
319                 serviceContext);
320         }
321 
322         session.setAttribute(WebKeys.OPEN_ID_LOGIN, new Long(user.getUserId()));
323 
324         return null;
325     }
326 
327     protected void sendOpenIdRequest(
328             ThemeDisplay themeDisplay, ActionRequest actionRequest,
329             ActionResponse actionResponse)
330         throws Exception {
331 
332         if (!OpenIdUtil.isEnabled(themeDisplay.getCompanyId())) {
333             return;
334         }
335 
336         HttpServletRequest request = PortalUtil.getHttpServletRequest(
337             actionRequest);
338         HttpServletResponse response = PortalUtil.getHttpServletResponse(
339             actionResponse);
340         HttpSession session = request.getSession();
341 
342         ActionResponseImpl actionResponseImpl =
343             (ActionResponseImpl)actionResponse;
344 
345         String openId = ParamUtil.getString(actionRequest, "openId");
346 
347         PortletURL portletURL = actionResponseImpl.createActionURL();
348 
349         portletURL.setParameter("struts_action", "/login/open_id");
350         portletURL.setParameter(Constants.CMD, Constants.READ);
351         portletURL.setParameter("saveLastPath", "0");
352 
353         ConsumerManager manager = OpenIdUtil.getConsumerManager();
354 
355         List<DiscoveryInformation> discoveries = manager.discover(openId);
356 
357         DiscoveryInformation discovered = manager.associate(discoveries);
358 
359         session.setAttribute(WebKeys.OPEN_ID_DISCO, discovered);
360 
361         AuthRequest authRequest = manager.authenticate(
362             discovered, portletURL.toString(), themeDisplay.getPortalURL());
363 
364         try {
365             UserLocalServiceUtil.getUserByOpenId(openId);
366         }
367         catch (NoSuchUserException nsue) {
368             String screenName = OpenIdUtil.getScreenName(openId);
369 
370             try {
371                 User user = UserLocalServiceUtil.getUserByScreenName(
372                     themeDisplay.getCompanyId(), screenName);
373 
374                 UserLocalServiceUtil.updateOpenId(user.getUserId(), openId);
375             }
376             catch (NoSuchUserException nsue2) {
377                 FetchRequest fetch = FetchRequest.createFetchRequest();
378 
379                 fetch.addAttribute(
380                     "email", "http://schema.openid.net/contact/email", true);
381                 fetch.addAttribute(
382                     "firstName", "http://schema.openid.net/namePerson/first",
383                     true);
384                 fetch.addAttribute(
385                     "lastName", "http://schema.openid.net/namePerson/last",
386                     true);
387 
388                 authRequest.addExtension(fetch);
389 
390                 SRegRequest sregRequest = SRegRequest.createFetchRequest();
391 
392                 sregRequest.addAttribute("fullname", true);
393                 sregRequest.addAttribute("email", true);
394 
395                 authRequest.addExtension(sregRequest);
396             }
397         }
398 
399         response.sendRedirect(authRequest.getDestinationUrl(true));
400     }
401 
402     private static final boolean _CHECK_METHOD_ON_PROCESS_ACTION = false;
403 
404     private static Log _log = LogFactoryUtil.getLog(OpenIdAction.class);
405 
406 }