001 /* Copyright 2006-2009 the original author or authors. 002 * 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. 014 */ 015 package org.codehaus.groovy.grails.plugins.springsecurity.facebook; 016 017 import javax.servlet.http.HttpServletRequest; 018 import javax.servlet.http.HttpServletResponse; 019 import javax.servlet.http.HttpSession; 020 021 import org.codehaus.groovy.grails.plugins.springsecurity.SecurityRequestHolder; 022 import org.springframework.security.Authentication; 023 import org.springframework.security.AuthenticationException; 024 import org.springframework.security.ui.AbstractProcessingFilter; 025 import org.springframework.security.ui.FilterChainOrder; 026 import org.springframework.security.ui.webapp.AuthenticationProcessingFilter; 027 import org.springframework.util.Assert; 028 import org.springframework.util.StringUtils; 029 import org.w3c.dom.Document; 030 031 import com.google.code.facebookapi.FacebookWebappHelper; 032 033 /** 034 * Intercepts j_spring_facebook_security_check to trigger Facebook login. 035 * 036 * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a> 037 */ 038 public class FacebookAuthenticationProcessingFilter extends AbstractProcessingFilter { 039 040 private String _apiKey; 041 private String _secretKey; 042 private String _authenticationUrlRoot; 043 044 /** 045 * {@inheritDoc} 046 * @see org.springframework.security.ui.AbstractProcessingFilter#attemptAuthentication( 047 * javax.servlet.http.HttpServletRequest) 048 */ 049 @Override 050 public Authentication attemptAuthentication(final HttpServletRequest request) throws AuthenticationException { 051 052 String authToken = request.getParameter("auth_token"); 053 if (!StringUtils.hasText(authToken)) { 054 // trigger a redirect to the Facebook login 055 throw new FacebookAuthenticationRequiredException(); 056 } 057 058 FacebookAuthenticationToken token = createToken( 059 authToken, request, SecurityRequestHolder.getResponse(), 060 _apiKey, _secretKey); 061 062 token.setDetails(authenticationDetailsSource.buildDetails(request)); 063 064 Authentication authentication = getAuthenticationManager().authenticate(token); 065 if (authentication.isAuthenticated()) { 066 setLastUsername(token.getUserId(), request); 067 } 068 069 return authentication; 070 } 071 072 private void setLastUsername(final long userId, final HttpServletRequest request) { 073 HttpSession session = request.getSession(false); 074 if (session != null || getAllowSessionCreation()) { 075 request.getSession().setAttribute( 076 AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY, 077 String.valueOf(userId)); 078 } 079 } 080 081 /** 082 * Build an authentication from a login <code>auth_token</code>. 083 * @param authToken the <code>auth_token</code> 084 * @param request the http request 085 * @param response the http response 086 * @param apiKey the API key 087 * @param secretKey the secret key 088 * @return the auth token 089 */ 090 protected FacebookAuthenticationToken createToken( 091 final String authToken, final HttpServletRequest request, final HttpServletResponse response, 092 final String apiKey, final String secretKey) { 093 094 try { 095 FacebookWebappHelper<Document> helper = FacebookWebappHelper.newInstanceXml( 096 request, response, apiKey, secretKey); 097 098 if (helper.isLogin()) { 099 String sessionKey = helper.doGetSession(authToken); 100 return new FacebookAuthenticationToken(helper.getUser().longValue(), sessionKey); 101 } 102 103 return new FacebookAuthenticationToken(FacebookAuthenticationToken.Status.failure, null); 104 } 105 catch (RuntimeException e) { 106 return new FacebookAuthenticationToken(FacebookAuthenticationToken.Status.error, e.getMessage()); 107 } 108 } 109 110 /** 111 * {@inheritDoc} 112 * @see org.springframework.security.ui.AbstractProcessingFilter#determineFailureUrl( 113 * javax.servlet.http.HttpServletRequest, org.springframework.security.AuthenticationException) 114 */ 115 @Override 116 protected String determineFailureUrl(final HttpServletRequest request, final AuthenticationException failed) { 117 if (failed instanceof FacebookAuthenticationRequiredException) { 118 return _authenticationUrlRoot + _apiKey; 119 } 120 121 return super.determineFailureUrl(request, failed); 122 } 123 124 /** 125 * {@inheritDoc} 126 * @see org.springframework.security.ui.AbstractProcessingFilter#getDefaultFilterProcessesUrl() 127 */ 128 @Override 129 public String getDefaultFilterProcessesUrl() { 130 return "/j_spring_facebook_security_check"; 131 } 132 133 /** 134 * {@inheritDoc} 135 * @see org.springframework.security.ui.SpringSecurityFilter#getOrder() 136 */ 137 public int getOrder() { 138 return FilterChainOrder.OPENID_PROCESSING_FILTER + 1; 139 } 140 141 /** 142 * Dependency injection for the API key. 143 * @param key the key 144 */ 145 public void setApiKey(final String key) { 146 _apiKey = key; 147 } 148 149 /** 150 * Dependency injection for the secret key. 151 * @param key the key 152 */ 153 public void setSecretKey(final String key) { 154 _secretKey = key; 155 } 156 157 /** 158 * Dependency injection for the Facebook auth url root. 159 * @param authenticationUrlRoot the url root 160 */ 161 public void setAuthenticationUrlRoot(String authenticationUrlRoot) { 162 _authenticationUrlRoot = authenticationUrlRoot; 163 } 164 165 /** 166 * {@inheritDoc} 167 * @see org.springframework.security.ui.AbstractProcessingFilter#afterPropertiesSet() 168 */ 169 @Override 170 public void afterPropertiesSet() throws Exception { 171 super.afterPropertiesSet(); 172 Assert.notNull(_apiKey, "API key must be specified"); 173 Assert.notNull(_secretKey, "Secret key must be specified"); 174 } 175 }