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.slf4j.Logger;
040    import org.slf4j.LoggerFactory;
041    
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        private static final Logger LOG = LoggerFactory.getLogger(RestFilter.class);
054    
055        private static final String HTTP_HEADER_DESTINATION = "Destination";
056        private static final String HTTP_METHOD_MOVE = "MOVE";
057        private static final String HTTP_METHOD_PUT = "PUT";
058        private static final String HTTP_METHOD_GET = "GET";
059        private static final String HTTP_METHOD_DELETE = "DELETE";
060    
061        private String readPermissionRole;
062        private String writePermissionRole;
063        private FilterConfig filterConfig;
064    
065        public void init(FilterConfig filterConfig) throws UnavailableException {
066            this.filterConfig = filterConfig;
067            readPermissionRole = filterConfig.getInitParameter("read-permission-role");
068            writePermissionRole = filterConfig.getInitParameter("write-permission-role");
069        }
070    
071        private File locateFile(HttpServletRequest request) {
072            return new File(filterConfig.getServletContext().getRealPath(request.getServletPath()), request.getPathInfo());
073        }
074    
075        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
076            if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) {
077                if (LOG.isDebugEnabled()) {
078                    LOG.debug("request not HTTP, can not understand: " + request.toString());
079                }
080                chain.doFilter(request, response);
081                return;
082            }
083    
084            HttpServletRequest httpRequest = (HttpServletRequest)request;
085            HttpServletResponse httpResponse = (HttpServletResponse)response;
086    
087            if (httpRequest.getMethod().equals(HTTP_METHOD_MOVE)) {
088                doMove(httpRequest, httpResponse);
089            } else if (httpRequest.getMethod().equals(HTTP_METHOD_PUT)) {
090                doPut(httpRequest, httpResponse);
091            } else if (httpRequest.getMethod().equals(HTTP_METHOD_GET)) {
092                if (checkGet(httpRequest, httpResponse)) {
093                    chain.doFilter(httpRequest, httpResponse); // actual processing
094                                                                // done elsewhere
095                }
096            } else if (httpRequest.getMethod().equals(HTTP_METHOD_DELETE)) {
097                doDelete(httpRequest, httpResponse);
098            } else {
099                chain.doFilter(httpRequest, httpResponse);
100            }
101        }
102    
103        protected void doMove(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
104            if (LOG.isDebugEnabled()) {
105                LOG.debug("RESTful file access: MOVE request for " + request.getRequestURI());
106            }
107    
108            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
109                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
110                return;
111            }
112    
113            File file = locateFile(request);
114            String destination = request.getHeader(HTTP_HEADER_DESTINATION);
115    
116            if (destination == null) {
117                response.sendError(HttpURLConnection.HTTP_BAD_REQUEST, "Destination header not found");
118                return;
119            }
120    
121            try {
122                URL destinationUrl = new URL(destination);
123                IOHelper.copyFile(file, new File(destinationUrl.getFile()));
124                IOHelper.deleteFile(file);
125            } catch (IOException e) {
126                response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // file
127                                                                            // could
128                                                                            // not
129                                                                            // be
130                                                                            // moved
131                return;
132            }
133    
134            response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return no
135                                                                    // content
136        }
137    
138        protected boolean checkGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
139            if (LOG.isDebugEnabled()) {
140                LOG.debug("RESTful file access: GET request for " + request.getRequestURI());
141            }
142    
143            if (readPermissionRole != null && !request.isUserInRole(readPermissionRole)) {
144                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
145                return false;
146            } else {
147                return true;
148            }
149        }
150    
151        protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
152            if (LOG.isDebugEnabled()) {
153                LOG.debug("RESTful file access: PUT request for " + request.getRequestURI());
154            }
155    
156            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
157                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
158                return;
159            }
160    
161            File file = locateFile(request);
162    
163            if (file.exists()) {
164                boolean success = file.delete(); // replace file if it exists
165                if (!success) {
166                    response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // file
167                                                                                // existed
168                                                                                // and
169                                                                                // could
170                                                                                // not
171                                                                                // be
172                                                                                // deleted
173                    return;
174                }
175            }
176    
177            FileOutputStream out = new FileOutputStream(file);
178            try {
179                IOHelper.copyInputStream(request.getInputStream(), out);
180            } catch (IOException e) {
181                LOG.warn("Exception occured" , e);
182                throw e;
183            } finally {
184                out.close();
185            }
186    
187            response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return no
188                                                                    // content
189        }
190    
191        protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
192            if (LOG.isDebugEnabled()) {
193                LOG.debug("RESTful file access: DELETE request for " + request.getRequestURI());
194            }
195    
196            if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
197                response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
198                return;
199            }
200    
201            File file = locateFile(request);
202    
203            if (!file.exists()) {
204                response.sendError(HttpURLConnection.HTTP_NOT_FOUND); // file not
205                                                                        // found
206                return;
207            }
208    
209            boolean success = IOHelper.deleteFile(file); // actual delete operation
210    
211            if (success) {
212                response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return
213                                                                        // no
214                                                                        // content
215            } else {
216                response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // could
217                                                                            // not
218                                                                            // be
219                                                                            // deleted
220                                                                            // due
221                                                                            // to
222                                                                            // internal
223                                                                            // error
224            }
225        }
226    
227        public void destroy() {
228            // nothing to destroy
229        }
230    }