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; 016 017 import java.io.IOException; 018 019 import javax.servlet.ServletRequest; 020 import javax.servlet.ServletResponse; 021 import javax.servlet.http.HttpServletRequest; 022 import javax.servlet.http.HttpServletResponse; 023 024 import org.springframework.beans.factory.InitializingBean; 025 import org.springframework.security.AccessDeniedException; 026 import org.springframework.security.Authentication; 027 import org.springframework.security.AuthenticationTrustResolver; 028 import org.springframework.security.AuthenticationTrustResolverImpl; 029 import org.springframework.security.context.SecurityContextHolder; 030 import org.springframework.security.ui.AbstractProcessingFilter; 031 import org.springframework.security.ui.AccessDeniedHandler; 032 import org.springframework.security.ui.savedrequest.SavedRequest; 033 import org.springframework.security.userdetails.UserDetails; 034 import org.springframework.security.util.PortResolver; 035 import org.springframework.util.Assert; 036 037 /** 038 * {@link AccessDeniedHandler} for redirect to errorPage (not RequestDispatcher#forward). 039 * 040 * @author T.Yamamoto 041 * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a> 042 */ 043 public class GrailsAccessDeniedHandlerImpl implements AccessDeniedHandler, InitializingBean { 044 045 private String errorPage; 046 private String ajaxErrorPage; 047 private String ajaxHeader = WithAjaxAuthenticationProcessingFilterEntryPoint.AJAX_HEADER; 048 private PortResolver portResolver; 049 private final AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); 050 051 /** 052 * {@inheritDoc} 053 * @see org.springframework.security.ui.AccessDeniedHandler#handle( 054 * javax.servlet.ServletRequest, javax.servlet.ServletResponse, 055 * org.springframework.security.AccessDeniedException) 056 */ 057 public void handle(final ServletRequest req, final ServletResponse res, final AccessDeniedException e) 058 throws IOException { 059 060 HttpServletRequest request = (HttpServletRequest)req; 061 HttpServletResponse response = (HttpServletResponse)res; 062 063 if (e != null && isLoggedIn() && authenticationTrustResolver.isRememberMe(getAuthentication())) { 064 // user has a cookie but is getting bounced because of IS_AUTHENTICATED_FULLY, 065 // so Acegi won't save the original request 066 request.getSession().setAttribute( 067 AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, 068 new SavedRequest(request, portResolver)); 069 } 070 071 if (errorPage != null || (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null)) { 072 boolean includePort = true; 073 String scheme = request.getScheme(); 074 String serverName = request.getServerName(); 075 int serverPort = portResolver.getServerPort(request); 076 String contextPath = request.getContextPath(); 077 boolean inHttp = "http".equals(scheme.toLowerCase()); 078 boolean inHttps = "https".equals(scheme.toLowerCase()); 079 080 if (inHttp && (serverPort == 80)) { 081 includePort = false; 082 } 083 else if (inHttps && (serverPort == 443)) { 084 includePort = false; 085 } 086 087 String commonRedirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") 088 + contextPath; 089 String redirectUrl = commonRedirectUrl; 090 if (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null) { 091 redirectUrl += ajaxErrorPage; 092 } 093 else if (errorPage != null) { 094 redirectUrl += errorPage; 095 } 096 else { 097 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); 098 } 099 100 response.sendRedirect(response.encodeRedirectURL(redirectUrl)); 101 } 102 103 if (!response.isCommitted()) { 104 // Send 403 (we do this after response has been written) 105 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); 106 } 107 } 108 109 private boolean isLoggedIn() { 110 if (getAuthentication() == null) { 111 return false; 112 } 113 return getAuthentication().getPrincipal() instanceof UserDetails; 114 } 115 116 private Authentication getAuthentication() { 117 return SecurityContextHolder.getContext() == null ? null 118 : SecurityContextHolder.getContext().getAuthentication(); 119 } 120 121 /** 122 * Dependency injection for the error page, e.g. '/login/denied'. 123 * @param page the page 124 */ 125 public void setErrorPage(final String page) { 126 if (page != null && !page.startsWith("/")) { 127 throw new IllegalArgumentException("ErrorPage must begin with '/'"); 128 } 129 errorPage = page; 130 } 131 132 /** 133 * Dependency injection for the Ajax error page, e.g. '/login/deniedAjax'. 134 * @param page the page 135 */ 136 public void setAjaxErrorPage(final String page) { 137 if (page != null && !page.startsWith("/")) { 138 throw new IllegalArgumentException("ErrorPage must begin with '/'"); 139 } 140 ajaxErrorPage = page; 141 } 142 143 /** 144 * Dependency injection for the Ajax header name; defaults to 'X-Requested-With'. 145 * @param header the header name 146 */ 147 public void setAjaxHeader(final String header) { 148 ajaxHeader = header; 149 } 150 151 /** 152 * Dependency injection for the port resolver. 153 * @param resolver the resolver 154 */ 155 public void setPortResolver(final PortResolver resolver) { 156 portResolver = resolver; 157 } 158 159 /** 160 * {@inheritDoc} 161 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 162 */ 163 public void afterPropertiesSet() { 164 Assert.notNull(ajaxHeader, "ajaxHeader is required"); 165 Assert.notNull(portResolver, "portResolver is required"); 166 } 167 }