From 22f792139406e381fdda5a02a61b61a577b33656 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Thu, 1 Feb 2018 14:12:06 +0500 Subject: Provide access to media files received from devices --- src/org/traccar/api/MediaFilter.java | 104 ++++++++++++++++++++++ src/org/traccar/protocol/Gt06ProtocolDecoder.java | 3 +- src/org/traccar/web/WebServer.java | 18 ++++ 3 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/org/traccar/api/MediaFilter.java diff --git a/src/org/traccar/api/MediaFilter.java b/src/org/traccar/api/MediaFilter.java new file mode 100644 index 000000000..c07175d40 --- /dev/null +++ b/src/org/traccar/api/MediaFilter.java @@ -0,0 +1,104 @@ +/* + * Copyright 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2018 Andrey Kunitsyn (andrey@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api; + +import java.io.IOException; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.ws.rs.NotAuthorizedException; + +import org.traccar.Context; +import org.traccar.api.resource.SessionResource; +import org.traccar.helper.Log; +import org.traccar.model.Device; + +public class MediaFilter implements Filter { + + private boolean dirAllowed; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + dirAllowed = Context.getConfig().getBoolean("media.dirAllowed"); + } + + private static void formatError(HttpServletResponse response, Exception e) throws IOException { + if (e instanceof SecurityException) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + } else if (e instanceof IllegalArgumentException) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else if (e instanceof NotAuthorizedException) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } else { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + response.getWriter().println(Log.exceptionStack(e)); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + try { + HttpSession session = ((HttpServletRequest) request).getSession(false); + Long userId = null; + if (session != null) { + userId = (Long) session.getAttribute(SessionResource.USER_ID_KEY); + if (userId != null) { + Context.getPermissionsManager().checkUserEnabled(userId); + Context.getStatisticsManager().registerRequest(userId); + } + } + if (userId == null) { + throw new NotAuthorizedException("Not authorized"); + } + + String[] parts = ((HttpServletRequest) request).getPathInfo().split("/"); + if (parts.length < 2) { + if (dirAllowed) { + Context.getPermissionsManager().checkAdmin(userId); + } else { + throw new SecurityException("Wrong path"); + } + } else if (parts.length == 2 && !dirAllowed) { + throw new SecurityException("Wrong path"); + } else { + Device device = Context.getIdentityManager().getByUniqueId(parts[1]); + if (device != null) { + Context.getPermissionsManager().checkDevice(userId, device.getId()); + } else { + throw new IllegalArgumentException("Device not found"); + } + } + + chain.doFilter(request, response); + } catch (Exception e) { + formatError((HttpServletResponse) response, e); + } + } + + @Override + public void destroy() { + } + +} diff --git a/src/org/traccar/protocol/Gt06ProtocolDecoder.java b/src/org/traccar/protocol/Gt06ProtocolDecoder.java index e11a6580b..4173b4d5b 100644 --- a/src/org/traccar/protocol/Gt06ProtocolDecoder.java +++ b/src/org/traccar/protocol/Gt06ProtocolDecoder.java @@ -704,7 +704,8 @@ public class Gt06ProtocolDecoder extends BaseProtocolDecoder { sendPhotoRequest(channel, pictureId); } else { Device device = Context.getDeviceManager().getById(deviceSession.getDeviceId()); - Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg"); + position.set( + Position.KEY_IMAGE, Context.getMediaManager().writeFile(device.getUniqueId(), photo, "jpg")); photos.remove(pictureId); } diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index e145ff554..8b0696081 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -25,6 +25,7 @@ import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.server.session.HashSessionManager; +import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.webapp.WebAppContext; @@ -35,6 +36,7 @@ import org.traccar.Config; import org.traccar.Context; import org.traccar.api.AsyncSocketServlet; import org.traccar.api.CorsResponseFilter; +import org.traccar.api.MediaFilter; import org.traccar.api.ObjectMapperProvider; import org.traccar.api.ResourceErrorHandler; import org.traccar.api.SecurityRequestFilter; @@ -42,6 +44,7 @@ import org.traccar.api.resource.ServerResource; import org.traccar.helper.Log; import javax.naming.InitialContext; +import javax.servlet.DispatcherType; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -49,6 +52,7 @@ import javax.sql.DataSource; import java.io.IOException; import java.io.Writer; import java.net.InetSocketAddress; +import java.util.EnumSet; public class WebServer { @@ -80,6 +84,7 @@ public class WebServer { } initServer(); + initMedia(); initApi(); if (config.getBoolean("web.console")) { initConsole(); @@ -156,6 +161,19 @@ public class WebServer { handlers.addHandler(app); } + private void initMedia() { + ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + servletHandler.setContextPath("/api/media"); + servletHandler.getSessionHandler().setSessionManager(sessionManager); + + ServletHolder servletHolder = new ServletHolder("media", DefaultServlet.class); + servletHolder.setInitParameter("resourceBase", config.getString("media.path")); + servletHolder.setInitParameter("dirAllowed", config.getString("media.dirAllowed", "false")); + servletHandler.addServlet(servletHolder, "/*"); + servletHandler.addFilter(MediaFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST)); + handlers.addHandler(servletHandler); + } + private void initApi() { ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); servletHandler.setContextPath("/api"); -- cgit v1.2.3 From 195ec5aed9e3a6a499cf5a21773235563806a0c1 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Fri, 2 Feb 2018 11:15:12 +0500 Subject: - Move media servlet to api context - Cleanup and optimization --- src/org/traccar/api/MediaFilter.java | 41 +++++++++++++----------------------- src/org/traccar/web/WebServer.java | 24 +++++++-------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/src/org/traccar/api/MediaFilter.java b/src/org/traccar/api/MediaFilter.java index c07175d40..b3dcbbd66 100644 --- a/src/org/traccar/api/MediaFilter.java +++ b/src/org/traccar/api/MediaFilter.java @@ -36,24 +36,8 @@ import org.traccar.model.Device; public class MediaFilter implements Filter { - private boolean dirAllowed; - @Override public void init(FilterConfig filterConfig) throws ServletException { - dirAllowed = Context.getConfig().getBoolean("media.dirAllowed"); - } - - private static void formatError(HttpServletResponse response, Exception e) throws IOException { - if (e instanceof SecurityException) { - response.setStatus(HttpServletResponse.SC_FORBIDDEN); - } else if (e instanceof IllegalArgumentException) { - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - } else if (e instanceof NotAuthorizedException) { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } else { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } - response.getWriter().println(Log.exceptionStack(e)); } @Override @@ -73,15 +57,10 @@ public class MediaFilter implements Filter { throw new NotAuthorizedException("Not authorized"); } - String[] parts = ((HttpServletRequest) request).getPathInfo().split("/"); - if (parts.length < 2) { - if (dirAllowed) { - Context.getPermissionsManager().checkAdmin(userId); - } else { - throw new SecurityException("Wrong path"); - } - } else if (parts.length == 2 && !dirAllowed) { - throw new SecurityException("Wrong path"); + String path = ((HttpServletRequest) request).getPathInfo(); + String[] parts = path.split("/"); + if (parts.length < 2 || parts.length == 2 && !path.endsWith("/")) { + Context.getPermissionsManager().checkAdmin(userId); } else { Device device = Context.getIdentityManager().getByUniqueId(parts[1]); if (device != null) { @@ -93,7 +72,17 @@ public class MediaFilter implements Filter { chain.doFilter(request, response); } catch (Exception e) { - formatError((HttpServletResponse) response, e); + HttpServletResponse httpResponse = (HttpServletResponse) response; + if (e instanceof SecurityException) { + httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + } else if (e instanceof IllegalArgumentException) { + httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else if (e instanceof NotAuthorizedException) { + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } else { + httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); + } + response.getWriter().println(Log.exceptionStack(e)); } } diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index 8b0696081..88b38c1ab 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 - 2016 Anton Tananaev (anton@traccar.org) + * Copyright 2012 - 2018 Anton Tananaev (anton@traccar.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -84,7 +84,6 @@ public class WebServer { } initServer(); - initMedia(); initApi(); if (config.getBoolean("web.console")) { initConsole(); @@ -161,19 +160,6 @@ public class WebServer { handlers.addHandler(app); } - private void initMedia() { - ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); - servletHandler.setContextPath("/api/media"); - servletHandler.getSessionHandler().setSessionManager(sessionManager); - - ServletHolder servletHolder = new ServletHolder("media", DefaultServlet.class); - servletHolder.setInitParameter("resourceBase", config.getString("media.path")); - servletHolder.setInitParameter("dirAllowed", config.getString("media.dirAllowed", "false")); - servletHandler.addServlet(servletHolder, "/*"); - servletHandler.addFilter(MediaFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST)); - handlers.addHandler(servletHandler); - } - private void initApi() { ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); servletHandler.setContextPath("/api"); @@ -181,11 +167,17 @@ public class WebServer { servletHandler.addServlet(new ServletHolder(new AsyncSocketServlet()), "/socket"); + ServletHolder servletHolder = new ServletHolder("media", DefaultServlet.class); + servletHolder.setInitParameter("resourceBase", config.getString("media.path")); + servletHolder.setInitParameter("dirAllowed", config.getString("media.directoryAllow", "false")); + servletHolder.setInitParameter("pathInfoOnly", "true"); + servletHandler.addServlet(servletHolder, "/media/*"); + servletHandler.addFilter(MediaFilter.class, "/media/*", EnumSet.allOf(DispatcherType.class)); + ResourceConfig resourceConfig = new ResourceConfig(); resourceConfig.registerClasses(JacksonFeature.class, ObjectMapperProvider.class, ResourceErrorHandler.class); resourceConfig.registerClasses(SecurityRequestFilter.class, CorsResponseFilter.class); resourceConfig.packages(ServerResource.class.getPackage().getName()); - servletHandler.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*"); handlers.addHandler(servletHandler); -- cgit v1.2.3 From 16f912fa4ebfbb89a5d99116c12740229fa09b67 Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Fri, 2 Feb 2018 11:41:54 +0500 Subject: Optimize exception handling --- src/org/traccar/api/MediaFilter.java | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/org/traccar/api/MediaFilter.java b/src/org/traccar/api/MediaFilter.java index b3dcbbd66..de15209e7 100644 --- a/src/org/traccar/api/MediaFilter.java +++ b/src/org/traccar/api/MediaFilter.java @@ -17,6 +17,7 @@ package org.traccar.api; import java.io.IOException; +import java.sql.SQLException; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -27,7 +28,6 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import javax.ws.rs.NotAuthorizedException; import org.traccar.Context; import org.traccar.api.resource.SessionResource; @@ -54,7 +54,9 @@ public class MediaFilter implements Filter { } } if (userId == null) { - throw new NotAuthorizedException("Not authorized"); + ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().println("Not authorized"); + return; } String path = ((HttpServletRequest) request).getPathInfo(); @@ -62,26 +64,22 @@ public class MediaFilter implements Filter { if (parts.length < 2 || parts.length == 2 && !path.endsWith("/")) { Context.getPermissionsManager().checkAdmin(userId); } else { - Device device = Context.getIdentityManager().getByUniqueId(parts[1]); + Device device = Context.getDeviceManager().getByUniqueId(parts[1]); if (device != null) { Context.getPermissionsManager().checkDevice(userId, device.getId()); } else { - throw new IllegalArgumentException("Device not found"); + ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_FOUND); + response.getWriter().println("Device not found"); + return; } } chain.doFilter(request, response); - } catch (Exception e) { - HttpServletResponse httpResponse = (HttpServletResponse) response; - if (e instanceof SecurityException) { - httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); - } else if (e instanceof IllegalArgumentException) { - httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND); - } else if (e instanceof NotAuthorizedException) { - httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } else { - httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); - } + } catch (SecurityException e) { + ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN); + response.getWriter().println(Log.exceptionStack(e)); + } catch (SQLException e) { + ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_BAD_REQUEST); response.getWriter().println(Log.exceptionStack(e)); } } -- cgit v1.2.3 From 1d56c58847d811086d65115d7dca7a705bfda92a Mon Sep 17 00:00:00 2001 From: Abyss777 Date: Fri, 2 Feb 2018 11:53:02 +0500 Subject: Remove custom text and repeated casting --- src/org/traccar/api/MediaFilter.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/org/traccar/api/MediaFilter.java b/src/org/traccar/api/MediaFilter.java index de15209e7..4685ce05b 100644 --- a/src/org/traccar/api/MediaFilter.java +++ b/src/org/traccar/api/MediaFilter.java @@ -43,6 +43,7 @@ public class MediaFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletResponse httpResponse = ((HttpServletResponse) response); try { HttpSession session = ((HttpServletRequest) request).getSession(false); Long userId = null; @@ -54,8 +55,7 @@ public class MediaFilter implements Filter { } } if (userId == null) { - ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.getWriter().println("Not authorized"); + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); return; } @@ -68,19 +68,18 @@ public class MediaFilter implements Filter { if (device != null) { Context.getPermissionsManager().checkDevice(userId, device.getId()); } else { - ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_FOUND); - response.getWriter().println("Device not found"); + httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND); return; } } chain.doFilter(request, response); } catch (SecurityException e) { - ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN); - response.getWriter().println(Log.exceptionStack(e)); + httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + httpResponse.getWriter().println(Log.exceptionStack(e)); } catch (SQLException e) { - ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_BAD_REQUEST); - response.getWriter().println(Log.exceptionStack(e)); + httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); + httpResponse.getWriter().println(Log.exceptionStack(e)); } } -- cgit v1.2.3