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 }