1 package org.apache.archiva.redback.struts2.interceptor;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import com.google.common.collect.Lists;
23 import com.opensymphony.xwork2.Action;
24 import com.opensymphony.xwork2.ActionContext;
25 import com.opensymphony.xwork2.ActionInvocation;
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.commons.lang.SystemUtils;
28 import org.apache.struts2.ServletActionContext;
29 import org.apache.archiva.redback.authorization.AuthorizationResult;
30 import org.apache.archiva.redback.system.SecuritySession;
31 import org.apache.archiva.redback.system.SecuritySystem;
32 import org.apache.archiva.redback.system.SecuritySystemConstants;
33 import org.apache.archiva.redback.integration.interceptor.SecureAction;
34 import org.apache.archiva.redback.integration.interceptor.SecureActionBundle;
35 import org.apache.archiva.redback.integration.interceptor.SecureActionException;
36 import org.springframework.context.annotation.Scope;
37 import org.springframework.stereotype.Controller;
39 import javax.inject.Inject;
40 import javax.servlet.ServletContext;
41 import javax.servlet.http.HttpSession;
42 import java.util.List;
45 * SecureActionInterceptor: Interceptor that will detect webwork actions that implement the SecureAction
46 * interface and providing they do verify that the current user is authorized to execute the action
48 * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
49 * @author Jesse McConnell <jesse@codehaus.org>
52 @Controller( "redbackSecureActionInterceptor" )
54 public class SecureActionInterceptor
55 extends AbstractHttpRequestTrackerInterceptor
57 private static final String REQUIRES_AUTHORIZATION = "requires-authorization";
59 private static final String REQUIRES_AUTHENTICATION = "requires-authentication";
61 private static final String HTTP_HEADER_REFERER = "Referer";
67 private SecuritySystem securitySystem;
72 private String trackerName = "simple";
74 private String enableReferrerCheck;
84 * process the action to determine if it implements SecureAction and then act
92 public String intercept( ActionInvocation invocation )
95 ActionContext context = ActionContext.getContext();
97 Action action = (Action) context.getActionInvocation().getAction();
99 logger.debug( "SecureActionInterceptor: processing {}", action.getClass().getName() );
101 if ( Boolean.valueOf( enableReferrerCheck ) )
103 logger.debug( "Referrer security check enabled." );
104 executeReferrerSecurityCheck();
109 if ( action instanceof SecureAction )
111 SecureAction secureAction = (SecureAction) action;
112 SecureActionBundle bundle = secureAction.getSecureActionBundle();
114 if ( bundle == null )
116 logger.error( "Null bundle detected." );
118 // TODO: send them somewhere else?
119 return invocation.invoke();
122 if ( bundle == SecureActionBundle.OPEN )
124 logger.debug( "Bundle.OPEN detected." );
126 return invocation.invoke();
129 SecuritySession session =
130 (SecuritySession) context.getSession().get( SecuritySystemConstants.SECURITY_SESSION_KEY );
132 // check the authentication requirements
133 if ( bundle.requiresAuthentication() )
135 if ( session == null || !session.isAuthenticated() )
137 logger.debug( "not authenticated, need to authenticate for this action" );
138 return processRequiresAuthentication( invocation );
142 List<SecureActionBundle.AuthorizationTuple> authzTuples = bundle.getAuthorizationTuples();
144 // if operations are returned we need to perform authorization checks
145 if ( authzTuples != null && authzTuples.size() > 0 )
147 // authn adds a session, if there is no session they are not authorized and authn is required for
148 // authz, even if it is just a guest user
149 if ( session == null )
151 logger.debug( "session required for authorization to run" );
152 return processRequiresAuthentication( invocation );
155 for ( SecureActionBundle.AuthorizationTuple tuple : authzTuples )
157 logger.debug( "checking authz for {}", tuple.toString() );
159 AuthorizationResult authzResult =
160 securitySystem.authorize( session, tuple.getOperation(), tuple.getResource() );
162 logger.debug( "checking the interceptor authz {} for {}", authzResult.isAuthorized(),
165 if ( authzResult.isAuthorized() )
167 if ( logger.isDebugEnabled() )
169 logger.debug( "{} is authorized for action {} by {}",
170 Lists.<Object>newArrayList( session.getUser().getPrincipal(),
171 secureAction.getClass().getName(),
172 tuple.toString() ) );
174 return invocation.invoke();
178 return processRequiresAuthorization( invocation );
183 logger.debug( "SecureActionInterceptor: {} not a secure action", action.getClass().getName() );
186 catch ( SecureActionException se )
188 logger.error( "can't generate the SecureActionBundle, deny access: " + se.getMessage() );
189 return processRequiresAuthentication( invocation );
192 logger.debug( "not a secure action {}", action.getClass().getName() );
193 String result = invocation.invoke();
194 logger.debug( "Passing invocation up, result is [{}] on call {}", result,
195 invocation.getAction().getClass().getName() );
199 private void executeReferrerSecurityCheck()
201 String referrer = ServletActionContext.getRequest().getHeader( HTTP_HEADER_REFERER );
203 logger.debug( "HTTP Referer header: {}", referrer );
205 String[] tokens = StringUtils.splitPreserveAllTokens( referrer, "/", 3 );
207 if ( tokens != null )
210 if ( tokens.length < 3 )
216 path = tokens[tokens.length - 1];
219 logger.debug( "Calculated virtual path: {}", path );
221 ServletContext servletContext = ServletActionContext.getServletContext();
223 String realPath = servletContext.getRealPath( path );
225 if ( StringUtils.isNotEmpty( realPath ) )
227 // on windows realPath can return full path c:\\bla\\bla\....
228 // so transforming \\ to /
229 if ( SystemUtils.IS_OS_WINDOWS )
231 realPath = StringUtils.replace( realPath, "\\", "/" );
233 if ( !realPath.endsWith( path ) )
235 String errorMsg = "Failed referrer security check: Request did not come from the same server. "
236 + "Detected HTTP Referer header is '" + referrer + "'.";
237 logger.error( errorMsg );
238 throw new RuntimeException( errorMsg );
242 logger.debug( "HTTP Referer header path found in server." );
248 logger.warn( "HTTP Referer header is null." );
252 protected String processRequiresAuthorization( ActionInvocation invocation )
254 addActionInvocation( invocation ).setBackTrack();
255 return REQUIRES_AUTHORIZATION;
258 protected String processRequiresAuthentication( ActionInvocation invocation )
260 HttpSession session = ServletActionContext.getRequest().getSession();
262 if ( session != null )
264 session.removeAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY );
267 addActionInvocation( invocation ).setBackTrack();
268 return REQUIRES_AUTHENTICATION;
271 public SecuritySystem getSecuritySystem()
273 return securitySystem;
276 public void setSecuritySystem( SecuritySystem securitySystem )
278 this.securitySystem = securitySystem;
281 protected String getTrackerName()
286 public String getEnableReferrerCheck()
288 return enableReferrerCheck;
291 public void setEnableReferrerCheck( String enableReferrerCheck )
293 this.enableReferrerCheck = enableReferrerCheck;
296 public void setTrackerName( String trackerName )
298 this.trackerName = trackerName;