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