001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    //========================================================================
018    //Copyright 2007 CSC - Scientific Computing Ltd.
019    //========================================================================
020    package org.apache.activemq.util;
021    
022    
023    import java.io.File;
024    import java.io.FileOutputStream;
025    import java.io.IOException;
026    import java.net.HttpURLConnection;
027    import java.net.URL;
028    
029    import javax.servlet.Filter;
030    import javax.servlet.FilterChain;
031    import javax.servlet.FilterConfig;
032    import javax.servlet.ServletException;
033    import javax.servlet.ServletRequest;
034    import javax.servlet.ServletResponse;
035    import javax.servlet.UnavailableException;
036    import javax.servlet.http.HttpServletRequest;
037    import javax.servlet.http.HttpServletResponse;
038    
039    import org.mortbay.log.Log;
040    import org.mortbay.util.IO;
041    import org.mortbay.util.URIUtil;
042    
043    /**
044     * <p>
045     * Adds support for HTTP PUT, MOVE and DELETE methods. If init parameters
046     * read-permission-role and write-permission-role are defined then all requests
047     * are authorized using the defined roles. Also GET methods are authorized.
048     * </p>
049     * 
050     * @author Aleksi Kallio
051     */
052    public class RestFilter implements Filter {
053    
054        private static final String HTTP_HEADER_DESTINATION = "Destination";
055        private static final String HTTP_METHOD_MOVE = "MOVE";
056        private static final String HTTP_METHOD_PUT = "PUT";
057        private static final String HTTP_METHOD_GET = "GET";
058        private static final String HTTP_METHOD_DELETE = "DELETE";
059    
060        private String readPermissionRole;
061        private String writePermissionRole;
062        private FilterConfig filterConfig;
063    
064        public void init(FilterConfig filterConfig) throws UnavailableException {
065            this.filterConfig = filterConfig;
066            readPermissionRole = filterConfig.getInitParameter("read-permission-role");
067            writePermissionRole = filterConfig.getInitParameter("write-permission-role");
068        }
069    
070        private File locateFile(HttpServletRequest request) {
071            return new File(filterConfig.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(), request.getPathInfo())));
072        }
073    
074        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
075            if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) {
076                if (Log.isDebugEnabled()) {
077                    Log.debug("request not HTTP, can not understand: " + request.toString());
078                }
079                chain.doFilter(request, response);
080                return;
081            }
082    
083            HttpServletRequest httpRequest = (HttpServletRequest)request;
084            HttpServletResponse httpResponse = (HttpServletResponse)response;
085    
086            if (httpRequest.getMethod().equals(HTTP_METHOD_MOVE)) {
087                doMove(httpRequest, httpResponse);
088            } else if (httpRequest.getMethod().equals(HTTP_METHOD_PUT)) {
089                doPut(httpRequest, httpResponse);
090            } else if (httpRequest.getMethod().equals(HTTP_METHOD_GET)) {
091                if (checkGet(httpRequest, httpResponse)) {
092                    chain.doFilter(httpRequest, httpResponse); // actual processing
093                                                                // done elsewhere
094                }
095            } else if (httpRequest.getMethod().equals(HTTP_METHOD_DELETE)) {
096                doDelete(httpRequest, httpResponse);
097            } else {
098                chain.doFilter(httpRequest, httpResponse);
099            }
100        }
101    
102        protected void doMove(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
103            if (Log.isDebugEnabled()) {
104                Log.debug("RESTful file access: MOVE request for " + request.getRequestURI());
105            }
106    
107            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
108                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
109                return;
110            }
111    
112            File file = locateFile(request);
113            String destination = request.getHeader(HTTP_HEADER_DESTINATION);
114    
115            if (destination == null) {
116                response.sendError(HttpURLConnection.HTTP_BAD_REQUEST, "Destination header not found");
117                return;
118            }
119    
120            try {
121                URL destinationUrl = new URL(destination);
122                IO.copyFile(file, new File(destinationUrl.getFile()));
123                IO.delete(file);
124            } catch (IOException e) {
125                response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // file
126                                                                            // could
127                                                                            // not
128                                                                            // be
129                                                                            // moved
130                return;
131            }
132    
133            response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return no
134                                                                    // content
135        }
136    
137        protected boolean checkGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
138            if (Log.isDebugEnabled()) {
139                Log.debug("RESTful file access: GET request for " + request.getRequestURI());
140            }
141    
142            if (readPermissionRole != null && !request.isUserInRole(readPermissionRole)) {
143                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
144                return false;
145            } else {
146                return true;
147            }
148        }
149    
150        protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
151            if (Log.isDebugEnabled()) {
152                Log.debug("RESTful file access: PUT request for " + request.getRequestURI());
153            }
154    
155            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
156                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
157                return;
158            }
159    
160            File file = locateFile(request);
161    
162            if (file.exists()) {
163                boolean success = file.delete(); // replace file if it exists
164                if (!success) {
165                    response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // file
166                                                                                // existed
167                                                                                // and
168                                                                                // could
169                                                                                // not
170                                                                                // be
171                                                                                // deleted
172                    return;
173                }
174            }
175    
176            FileOutputStream out = new FileOutputStream(file);
177            try {
178                IO.copy(request.getInputStream(), out);
179            } catch (IOException e) {
180                Log.warn(Log.EXCEPTION, e); // is this obsolete?
181                out.close();
182                throw e;
183            }
184    
185            response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return no
186                                                                    // content
187        }
188    
189        protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
190            if (Log.isDebugEnabled()) {
191                Log.debug("RESTful file access: DELETE request for " + request.getRequestURI());
192            }
193    
194            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
195                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
196                return;
197            }
198    
199            File file = locateFile(request);
200    
201            if (!file.exists()) {
202                response.sendError(HttpURLConnection.HTTP_NOT_FOUND); // file not
203                                                                        // found
204                return;
205            }
206    
207            boolean success = IO.delete(file); // actual delete operation
208    
209            if (success) {
210                response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return
211                                                                        // no
212                                                                        // content
213            } else {
214                response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // could
215                                                                            // not
216                                                                            // be
217                                                                            // deleted
218                                                                            // due
219                                                                            // to
220                                                                            // internal
221                                                                            // error
222            }
223        }
224    
225        public void destroy() {
226            // nothing to destroy
227        }
228    }