]> source.dussan.org Git - archiva.git/blob
5bd7e1df83a2b550eaf345cdd05e6fa2f11d4de0
[archiva.git] /
1 package org.apache.archiva.redback.struts2.interceptor;
2
3 /*
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
38
39 import javax.inject.Inject;
40 import javax.servlet.ServletContext;
41 import javax.servlet.http.HttpSession;
42 import java.util.List;
43
44 /**
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
47  *
48  * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
49  * @author Jesse McConnell <jesse@codehaus.org>
50  * @version $Id$
51  */
52 @Controller( "redbackSecureActionInterceptor" )
53 @Scope( "prototype" )
54 public class SecureActionInterceptor
55     extends AbstractHttpRequestTrackerInterceptor
56 {
57     private static final String REQUIRES_AUTHORIZATION = "requires-authorization";
58
59     private static final String REQUIRES_AUTHENTICATION = "requires-authentication";
60
61     private static final String HTTP_HEADER_REFERER = "Referer";
62
63     /**
64      *
65      */
66     @Inject
67     private SecuritySystem securitySystem;
68
69     /**
70      *
71      */
72     private String trackerName = "simple";
73
74     private String enableReferrerCheck;
75
76     @Override
77     public void destroy()
78     {
79         // noop
80     }
81
82
83     /**
84      * process the action to determine if it implements SecureAction and then act
85      * accordingly
86      *
87      * @param invocation
88      * @return
89      * @throws Exception
90      */
91     @Override
92     public String intercept( ActionInvocation invocation )
93         throws Exception
94     {
95         ActionContext context = ActionContext.getContext();
96
97         Action action = (Action) context.getActionInvocation().getAction();
98
99         logger.debug( "SecureActionInterceptor: processing {}", action.getClass().getName() );
100
101         if ( Boolean.valueOf( enableReferrerCheck ) )
102         {
103             logger.debug( "Referrer security check enabled." );
104             executeReferrerSecurityCheck();
105         }
106
107         try
108         {
109             if ( action instanceof SecureAction )
110             {
111                 SecureAction secureAction = (SecureAction) action;
112                 SecureActionBundle bundle = secureAction.getSecureActionBundle();
113
114                 if ( bundle == null )
115                 {
116                     logger.error( "Null bundle detected." );
117
118                     // TODO: send them somewhere else?
119                     return invocation.invoke();
120                 }
121
122                 if ( bundle == SecureActionBundle.OPEN )
123                 {
124                     logger.debug( "Bundle.OPEN detected." );
125
126                     return invocation.invoke();
127                 }
128
129                 SecuritySession session =
130                     (SecuritySession) context.getSession().get( SecuritySystemConstants.SECURITY_SESSION_KEY );
131
132                 // check the authentication requirements
133                 if ( bundle.requiresAuthentication() )
134                 {
135                     if ( session == null || !session.isAuthenticated() )
136                     {
137                         logger.debug( "not authenticated, need to authenticate for this action" );
138                         return processRequiresAuthentication( invocation );
139                     }
140                 }
141
142                 List<SecureActionBundle.AuthorizationTuple> authzTuples = bundle.getAuthorizationTuples();
143
144                 // if operations are returned we need to perform authorization checks
145                 if ( authzTuples != null && authzTuples.size() > 0 )
146                 {
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 )
150                     {
151                         logger.debug( "session required for authorization to run" );
152                         return processRequiresAuthentication( invocation );
153                     }
154
155                     for ( SecureActionBundle.AuthorizationTuple tuple : authzTuples )
156                     {
157                         logger.debug( "checking authz for {}", tuple.toString() );
158
159                         AuthorizationResult authzResult =
160                             securitySystem.authorize( session, tuple.getOperation(), tuple.getResource() );
161
162                         logger.debug( "checking the interceptor authz {} for {}", authzResult.isAuthorized(),
163                                       tuple.toString() );
164
165                         if ( authzResult.isAuthorized() )
166                         {
167                             if ( logger.isDebugEnabled() )
168                             {
169                                 logger.debug( "{} is authorized for action {} by {}",
170                                               Lists.<Object>newArrayList( session.getUser().getPrincipal(),
171                                                                           secureAction.getClass().getName(),
172                                                                           tuple.toString() ) );
173                             }
174                             return invocation.invoke();
175                         }
176                     }
177
178                     return processRequiresAuthorization( invocation );
179                 }
180             }
181             else
182             {
183                 logger.debug( "SecureActionInterceptor: {} not a secure action", action.getClass().getName() );
184             }
185         }
186         catch ( SecureActionException se )
187         {
188             logger.error( "can't generate the SecureActionBundle, deny access: " + se.getMessage() );
189             return processRequiresAuthentication( invocation );
190         }
191
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() );
196         return result;
197     }
198
199     private void executeReferrerSecurityCheck()
200     {
201         String referrer = ServletActionContext.getRequest().getHeader( HTTP_HEADER_REFERER );
202
203         logger.debug( "HTTP Referer header: {}", referrer );
204
205         String[] tokens = StringUtils.splitPreserveAllTokens( referrer, "/", 3 );
206
207         if ( tokens != null )
208         {
209             String path;
210             if ( tokens.length < 3 )
211             {
212                 path = referrer;
213             }
214             else
215             {
216                 path = tokens[tokens.length - 1];
217             }
218
219             logger.debug( "Calculated virtual path: {}", path );
220
221             ServletContext servletContext = ServletActionContext.getServletContext();
222
223             String realPath = servletContext.getRealPath( path );
224
225             if ( StringUtils.isNotEmpty( realPath ) )
226             {
227                 // on windows realPath can return full path c:\\bla\\bla\....
228                 // so transforming \\ to /
229                 if ( SystemUtils.IS_OS_WINDOWS )
230                 {
231                     realPath = StringUtils.replace( realPath, "\\", "/" );
232                 }
233                 if ( !realPath.endsWith( path ) )
234                 {
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 );
239                 }
240                 else
241                 {
242                     logger.debug( "HTTP Referer header path found in server." );
243                 }
244             }
245         }
246         else
247         {
248             logger.warn( "HTTP Referer header is null." );
249         }
250     }
251
252     protected String processRequiresAuthorization( ActionInvocation invocation )
253     {
254         addActionInvocation( invocation ).setBackTrack();
255         return REQUIRES_AUTHORIZATION;
256     }
257
258     protected String processRequiresAuthentication( ActionInvocation invocation )
259     {
260         HttpSession session = ServletActionContext.getRequest().getSession();
261
262         if ( session != null )
263         {
264             session.removeAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY );
265         }
266
267         addActionInvocation( invocation ).setBackTrack();
268         return REQUIRES_AUTHENTICATION;
269     }
270
271     public SecuritySystem getSecuritySystem()
272     {
273         return securitySystem;
274     }
275
276     public void setSecuritySystem( SecuritySystem securitySystem )
277     {
278         this.securitySystem = securitySystem;
279     }
280
281     protected String getTrackerName()
282     {
283         return trackerName;
284     }
285
286     public String getEnableReferrerCheck()
287     {
288         return enableReferrerCheck;
289     }
290
291     public void setEnableReferrerCheck( String enableReferrerCheck )
292     {
293         this.enableReferrerCheck = enableReferrerCheck;
294     }
295
296     public void setTrackerName( String trackerName )
297     {
298         this.trackerName = trackerName;
299     }
300 }