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//========================================================================
020package org.apache.activemq.util;
021
022
023import java.io.File;
024import java.io.FileOutputStream;
025import java.io.IOException;
026import java.net.HttpURLConnection;
027import java.net.URL;
028
029import javax.servlet.Filter;
030import javax.servlet.FilterChain;
031import javax.servlet.FilterConfig;
032import javax.servlet.ServletException;
033import javax.servlet.ServletRequest;
034import javax.servlet.ServletResponse;
035import javax.servlet.UnavailableException;
036import javax.servlet.http.HttpServletRequest;
037import javax.servlet.http.HttpServletResponse;
038
039import org.slf4j.Logger;
040import 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 */
052public 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            out.close();
183            throw e;
184        }
185
186        response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return no
187                                                                // content
188    }
189
190    protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
191        if (LOG.isDebugEnabled()) {
192            LOG.debug("RESTful file access: DELETE request for " + request.getRequestURI());
193        }
194
195        if (writePermissionRole != null && !request.isUserInRole(writePermissionRole)) {
196            response.sendError(HttpURLConnection.HTTP_FORBIDDEN);
197            return;
198        }
199
200        File file = locateFile(request);
201
202        if (!file.exists()) {
203            response.sendError(HttpURLConnection.HTTP_NOT_FOUND); // file not
204                                                                    // found
205            return;
206        }
207
208        boolean success = IOHelper.deleteFile(file); // actual delete operation
209
210        if (success) {
211            response.setStatus(HttpURLConnection.HTTP_NO_CONTENT); // we return
212                                                                    // no
213                                                                    // content
214        } else {
215            response.sendError(HttpURLConnection.HTTP_INTERNAL_ERROR); // could
216                                                                        // not
217                                                                        // be
218                                                                        // deleted
219                                                                        // due
220                                                                        // to
221                                                                        // internal
222                                                                        // error
223        }
224    }
225
226    public void destroy() {
227        // nothing to destroy
228    }
229}