001 /* 002 * Copyright 2007 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.codehaus.groovy.grails.plugins.springsecurity; 017 018 import java.io.IOException; 019 020 import javax.servlet.ServletRequest; 021 import javax.servlet.ServletResponse; 022 import javax.servlet.http.HttpServletRequest; 023 import javax.servlet.http.HttpServletResponse; 024 025 import org.springframework.security.AccessDeniedException; 026 import org.springframework.security.ui.AbstractProcessingFilter; 027 import org.springframework.security.ui.AccessDeniedHandler; 028 import org.springframework.security.ui.savedrequest.SavedRequest; 029 import org.springframework.security.util.PortResolver; 030 import org.springframework.security.util.PortResolverImpl; 031 import org.springframework.security.context.SecurityContextHolder; 032 033 /** 034 * AccessDeniedHandler for redirect to errorPage (not RequestDispatcher#forward). 035 * 036 * @author T.Yamamoto 037 * @author <a href='mailto:beckwithb@studentsonly.com'>Burt Beckwith</a> 038 */ 039 public class GrailsAccessDeniedHandlerImpl implements AccessDeniedHandler { 040 041 private String errorPage; 042 private String ajaxErrorPage; 043 private String ajaxHeader = WithAjaxAuthenticationProcessingFilterEntryPoint.AJAX_HEADER; 044 private PortResolver portResolver = new PortResolverImpl(); 045 046 /** 047 * {@inheritDoc} 048 * @see org.springframework.security.ui.AccessDeniedHandler#handle( 049 * javax.servlet.ServletRequest, javax.servlet.ServletResponse, 050 * org.springframework.security.AccessDeniedException) 051 */ 052 public void handle(final ServletRequest req, final ServletResponse res, final AccessDeniedException e) 053 throws IOException { 054 055 HttpServletRequest request = (HttpServletRequest)req; 056 HttpServletResponse response = (HttpServletResponse)res; 057 058 if (e != null && isLoggedIn()) { 059 // user has a cookie but is getting bounced because of IS_AUTHENTICATED_FULLY, 060 // so Acegi won't save the original request 061 request.getSession().setAttribute( 062 AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY, 063 new SavedRequest(request, portResolver)); 064 } 065 066 if (errorPage != null || (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null)) { 067 boolean includePort = true; 068 String scheme = request.getScheme(); 069 String serverName = request.getServerName(); 070 int serverPort = portResolver.getServerPort(request); 071 String contextPath = request.getContextPath(); 072 boolean inHttp = "http".equals(scheme.toLowerCase()); 073 boolean inHttps = "https".equals(scheme.toLowerCase()); 074 075 if (inHttp && (serverPort == 80)) { 076 includePort = false; 077 } 078 else if (inHttps && (serverPort == 443)) { 079 includePort = false; 080 } 081 082 String commonRedirectUrl = scheme + "://" + serverName + ((includePort) ? (":" + serverPort) : "") 083 + contextPath; 084 String redirectUrl = commonRedirectUrl; 085 if (ajaxErrorPage != null && request.getHeader(ajaxHeader) != null) { 086 redirectUrl += ajaxErrorPage; 087 } 088 else if (errorPage != null) { 089 redirectUrl += errorPage; 090 } 091 else { 092 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); 093 } 094 095 response.sendRedirect(response.encodeRedirectURL(redirectUrl)); 096 } 097 098 if (!response.isCommitted()) { 099 // Send 403 (we do this after response has been written) 100 response.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage()); 101 } 102 } 103 104 private boolean isLoggedIn() { 105 if (SecurityContextHolder.getContext() == null 106 || SecurityContextHolder.getContext().getAuthentication() == null) { 107 return false; 108 } 109 return !(SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String); 110 } 111 112 /** 113 * Dependency injection for the error page, e.g. '/login/denied'. 114 * @param page the page 115 */ 116 public void setErrorPage(final String page) { 117 if (page != null && !page.startsWith("/")) { 118 throw new IllegalArgumentException("ErrorPage must begin with '/'"); 119 } 120 errorPage = page; 121 } 122 123 /** 124 * Dependency injection for the Ajax error page, e.g. '/login/deniedAjax'. 125 * @param page the page 126 */ 127 public void setAjaxErrorPage(final String page) { 128 if (page != null && !page.startsWith("/")) { 129 throw new IllegalArgumentException("ErrorPage must begin with '/'"); 130 } 131 ajaxErrorPage = page; 132 } 133 134 /** 135 * Dependency injection for the Ajax header name; defaults to 'X-Requested-With'. 136 * @param header the header name 137 */ 138 public void setAjaxHeader(final String header) { 139 ajaxHeader = header; 140 } 141 }