diff options
Diffstat (limited to 'src/main/java/org/traccar/api')
30 files changed, 2173 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/api/AsyncSocket.java b/src/main/java/org/traccar/api/AsyncSocket.java new file mode 100644 index 000000000..906d16b5b --- /dev/null +++ b/src/main/java/org/traccar/api/AsyncSocket.java @@ -0,0 +1,96 @@ +/* + * Copyright 2015 - 2016 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. + * 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 com.fasterxml.jackson.core.JsonProcessingException; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Context; +import org.traccar.database.ConnectionManager; +import org.traccar.model.Device; +import org.traccar.model.Event; +import org.traccar.model.Position; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class AsyncSocket extends WebSocketAdapter implements ConnectionManager.UpdateListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(AsyncSocket.class); + + private static final String KEY_DEVICES = "devices"; + private static final String KEY_POSITIONS = "positions"; + private static final String KEY_EVENTS = "events"; + + private long userId; + + public AsyncSocket(long userId) { + this.userId = userId; + } + + @Override + public void onWebSocketConnect(Session session) { + super.onWebSocketConnect(session); + + Map<String, Collection<?>> data = new HashMap<>(); + data.put(KEY_POSITIONS, Context.getDeviceManager().getInitialState(userId)); + sendData(data); + + Context.getConnectionManager().addListener(userId, this); + } + + @Override + public void onWebSocketClose(int statusCode, String reason) { + super.onWebSocketClose(statusCode, reason); + + Context.getConnectionManager().removeListener(userId, this); + } + + @Override + public void onUpdateDevice(Device device) { + Map<String, Collection<?>> data = new HashMap<>(); + data.put(KEY_DEVICES, Collections.singletonList(device)); + sendData(data); + } + + @Override + public void onUpdatePosition(Position position) { + Map<String, Collection<?>> data = new HashMap<>(); + data.put(KEY_POSITIONS, Collections.singletonList(position)); + sendData(data); + } + + @Override + public void onUpdateEvent(Event event) { + Map<String, Collection<?>> data = new HashMap<>(); + data.put(KEY_EVENTS, Collections.singletonList(event)); + sendData(data); + } + + private void sendData(Map<String, Collection<?>> data) { + if (!data.isEmpty() && isConnected()) { + try { + getRemote().sendString(Context.getObjectMapper().writeValueAsString(data), null); + } catch (JsonProcessingException e) { + LOGGER.warn("Socket JSON formatting error", e); + } + } + } +} diff --git a/src/main/java/org/traccar/api/AsyncSocketServlet.java b/src/main/java/org/traccar/api/AsyncSocketServlet.java new file mode 100644 index 000000000..9318b6fc6 --- /dev/null +++ b/src/main/java/org/traccar/api/AsyncSocketServlet.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 - 2016 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. + * 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 org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; +import org.eclipse.jetty.websocket.servlet.WebSocketCreator; +import org.eclipse.jetty.websocket.servlet.WebSocketServlet; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; +import org.traccar.Context; +import org.traccar.api.resource.SessionResource; + +public class AsyncSocketServlet extends WebSocketServlet { + + private static final long ASYNC_TIMEOUT = 10 * 60 * 1000; + + @Override + public void configure(WebSocketServletFactory factory) { + factory.getPolicy().setIdleTimeout(Context.getConfig().getLong("web.timeout", ASYNC_TIMEOUT)); + factory.setCreator(new WebSocketCreator() { + @Override + public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) { + if (req.getSession() != null) { + long userId = (Long) req.getSession().getAttribute(SessionResource.USER_ID_KEY); + return new AsyncSocket(userId); + } else { + return null; + } + } + }); + } + +} diff --git a/src/main/java/org/traccar/api/BaseObjectResource.java b/src/main/java/org/traccar/api/BaseObjectResource.java new file mode 100644 index 000000000..7de6a3877 --- /dev/null +++ b/src/main/java/org/traccar/api/BaseObjectResource.java @@ -0,0 +1,176 @@ +/* + * Copyright 2017 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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.sql.SQLException; +import java.util.Set; + +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Response; + +import org.traccar.Context; +import org.traccar.database.BaseObjectManager; +import org.traccar.database.ExtendedObjectManager; +import org.traccar.database.ManagableObjects; +import org.traccar.database.SimpleObjectManager; +import org.traccar.helper.LogAction; +import org.traccar.model.BaseModel; +import org.traccar.model.Calendar; +import org.traccar.model.Command; +import org.traccar.model.Device; +import org.traccar.model.Group; +import org.traccar.model.GroupedModel; +import org.traccar.model.ScheduledModel; +import org.traccar.model.User; + +public abstract class BaseObjectResource<T extends BaseModel> extends BaseResource { + + private Class<T> baseClass; + + public BaseObjectResource(Class<T> baseClass) { + this.baseClass = baseClass; + } + + protected final Class<T> getBaseClass() { + return baseClass; + } + + protected final Set<Long> getSimpleManagerItems(BaseObjectManager<T> manager, boolean all, long userId) { + Set<Long> result = null; + if (all) { + if (Context.getPermissionsManager().getUserAdmin(getUserId())) { + result = manager.getAllItems(); + } else { + Context.getPermissionsManager().checkManager(getUserId()); + result = ((ManagableObjects) manager).getManagedItems(getUserId()); + } + } else { + if (userId == 0) { + userId = getUserId(); + } + Context.getPermissionsManager().checkUser(getUserId(), userId); + result = ((ManagableObjects) manager).getUserItems(userId); + } + return result; + } + + @POST + public Response add(T entity) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + if (baseClass.equals(Device.class)) { + Context.getPermissionsManager().checkDeviceReadonly(getUserId()); + Context.getPermissionsManager().checkDeviceLimit(getUserId()); + } else if (baseClass.equals(Command.class)) { + Context.getPermissionsManager().checkLimitCommands(getUserId()); + } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { + Context.getPermissionsManager().checkPermission( + Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); + } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { + Context.getPermissionsManager().checkPermission( + Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); + } + + BaseObjectManager<T> manager = Context.getManager(baseClass); + manager.addItem(entity); + LogAction.create(getUserId(), entity); + + Context.getDataManager().linkObject(User.class, getUserId(), baseClass, entity.getId(), true); + LogAction.link(getUserId(), User.class, getUserId(), baseClass, entity.getId()); + + if (manager instanceof SimpleObjectManager) { + ((SimpleObjectManager<T>) manager).refreshUserItems(); + } else if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) { + Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); + Context.getPermissionsManager().refreshAllExtendedPermissions(); + } + return Response.ok(entity).build(); + } + + @Path("{id}") + @PUT + public Response update(T entity) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + if (baseClass.equals(Device.class)) { + Context.getPermissionsManager().checkDeviceReadonly(getUserId()); + } else if (baseClass.equals(User.class)) { + User before = Context.getPermissionsManager().getUser(entity.getId()); + Context.getPermissionsManager().checkUserUpdate(getUserId(), before, (User) entity); + } else if (baseClass.equals(Command.class)) { + Context.getPermissionsManager().checkLimitCommands(getUserId()); + } else if (entity instanceof GroupedModel && ((GroupedModel) entity).getGroupId() != 0) { + Context.getPermissionsManager().checkPermission( + Group.class, getUserId(), ((GroupedModel) entity).getGroupId()); + } else if (entity instanceof ScheduledModel && ((ScheduledModel) entity).getCalendarId() != 0) { + Context.getPermissionsManager().checkPermission( + Calendar.class, getUserId(), ((ScheduledModel) entity).getCalendarId()); + } + Context.getPermissionsManager().checkPermission(baseClass, getUserId(), entity.getId()); + + Context.getManager(baseClass).updateItem(entity); + LogAction.edit(getUserId(), entity); + + if (baseClass.equals(Group.class) || baseClass.equals(Device.class)) { + Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); + Context.getPermissionsManager().refreshAllExtendedPermissions(); + } + return Response.ok(entity).build(); + } + + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) throws SQLException { + Context.getPermissionsManager().checkReadonly(getUserId()); + if (baseClass.equals(Device.class)) { + Context.getPermissionsManager().checkDeviceReadonly(getUserId()); + } else if (baseClass.equals(Command.class)) { + Context.getPermissionsManager().checkLimitCommands(getUserId()); + } + Context.getPermissionsManager().checkPermission(baseClass, getUserId(), id); + + BaseObjectManager<T> manager = Context.getManager(baseClass); + manager.removeItem(id); + LogAction.remove(getUserId(), baseClass, id); + + if (manager instanceof SimpleObjectManager) { + ((SimpleObjectManager<T>) manager).refreshUserItems(); + if (manager instanceof ExtendedObjectManager) { + ((ExtendedObjectManager<T>) manager).refreshExtendedPermissions(); + } + } + if (baseClass.equals(Group.class) || baseClass.equals(Device.class) || baseClass.equals(User.class)) { + if (baseClass.equals(Group.class)) { + Context.getGroupsManager().updateGroupCache(true); + Context.getDeviceManager().updateDeviceCache(true); + } + Context.getPermissionsManager().refreshDeviceAndGroupPermissions(); + if (baseClass.equals(User.class)) { + Context.getPermissionsManager().refreshAllUsersPermissions(); + } else { + Context.getPermissionsManager().refreshAllExtendedPermissions(); + } + } else if (baseClass.equals(Calendar.class)) { + Context.getGeofenceManager().refreshItems(); + Context.getNotificationManager().refreshItems(); + } + return Response.noContent().build(); + } + +} diff --git a/src/main/java/org/traccar/api/BaseResource.java b/src/main/java/org/traccar/api/BaseResource.java new file mode 100644 index 000000000..cc272df9c --- /dev/null +++ b/src/main/java/org/traccar/api/BaseResource.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 - 2017 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. + * 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 javax.ws.rs.core.SecurityContext; + +public class BaseResource { + + @javax.ws.rs.core.Context + private SecurityContext securityContext; + + protected long getUserId() { + UserPrincipal principal = (UserPrincipal) securityContext.getUserPrincipal(); + if (principal != null) { + return principal.getUserId(); + } + return 0; + } +} diff --git a/src/main/java/org/traccar/api/CorsResponseFilter.java b/src/main/java/org/traccar/api/CorsResponseFilter.java new file mode 100644 index 000000000..227f80609 --- /dev/null +++ b/src/main/java/org/traccar/api/CorsResponseFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 - 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. + * 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 io.netty.handler.codec.http.HttpHeaderNames; +import org.traccar.Context; + +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import java.io.IOException; + +public class CorsResponseFilter implements ContainerResponseFilter { + + private static final String ORIGIN_ALL = "*"; + private static final String HEADERS_ALL = "origin, content-type, accept, authorization"; + private static final String METHODS_ALL = "GET, POST, PUT, DELETE, OPTIONS"; + + @Override + public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { + if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString())) { + response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString(), HEADERS_ALL); + } + + if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS.toString())) { + response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS.toString(), true); + } + + if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString())) { + response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString(), METHODS_ALL); + } + + if (!response.getHeaders().containsKey(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString())) { + String origin = request.getHeaderString(HttpHeaderNames.ORIGIN.toString()); + String allowed = Context.getConfig().getString("web.origin"); + + if (origin == null) { + response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), ORIGIN_ALL); + } else if (allowed == null || allowed.equals(ORIGIN_ALL) || allowed.contains(origin)) { + response.getHeaders().add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(), origin); + } + } + } + +} diff --git a/src/main/java/org/traccar/api/ExtendedObjectResource.java b/src/main/java/org/traccar/api/ExtendedObjectResource.java new file mode 100644 index 000000000..007a7b1bd --- /dev/null +++ b/src/main/java/org/traccar/api/ExtendedObjectResource.java @@ -0,0 +1,62 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 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.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; + +import org.traccar.Context; +import org.traccar.database.ExtendedObjectManager; +import org.traccar.model.BaseModel; + +public class ExtendedObjectResource<T extends BaseModel> extends BaseObjectResource<T> { + + public ExtendedObjectResource(Class<T> baseClass) { + super(baseClass); + } + + @GET + public Collection<T> get( + @QueryParam("all") boolean all, @QueryParam("userId") long userId, @QueryParam("groupId") long groupId, + @QueryParam("deviceId") long deviceId, @QueryParam("refresh") boolean refresh) throws SQLException { + + ExtendedObjectManager<T> manager = (ExtendedObjectManager<T>) Context.getManager(getBaseClass()); + if (refresh) { + manager.refreshItems(); + } + + Set<Long> result = new HashSet<>(getSimpleManagerItems(manager, all, userId)); + + if (groupId != 0) { + Context.getPermissionsManager().checkGroup(getUserId(), groupId); + result.retainAll(manager.getGroupItems(groupId)); + } + + if (deviceId != 0) { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + result.retainAll(manager.getDeviceItems(deviceId)); + } + return manager.getItems(result); + + } + +} diff --git a/src/main/java/org/traccar/api/MediaFilter.java b/src/main/java/org/traccar/api/MediaFilter.java new file mode 100644 index 000000000..53539770f --- /dev/null +++ b/src/main/java/org/traccar/api/MediaFilter.java @@ -0,0 +1,92 @@ +/* + * 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 java.sql.SQLException; + +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 org.traccar.Context; +import org.traccar.Main; +import org.traccar.api.resource.SessionResource; +import org.traccar.database.StatisticsManager; +import org.traccar.helper.Log; +import org.traccar.model.Device; + +public class MediaFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @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; + if (session != null) { + userId = (Long) session.getAttribute(SessionResource.USER_ID_KEY); + if (userId != null) { + Context.getPermissionsManager().checkUserEnabled(userId); + Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId); + } + } + if (userId == null) { + httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + 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.getDeviceManager().getByUniqueId(parts[1]); + if (device != null) { + Context.getPermissionsManager().checkDevice(userId, device.getId()); + } else { + httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + } + + chain.doFilter(request, response); + } catch (SecurityException e) { + httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + httpResponse.getWriter().println(Log.exceptionStack(e)); + } catch (SQLException e) { + httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); + httpResponse.getWriter().println(Log.exceptionStack(e)); + } + } + + @Override + public void destroy() { + } + +} diff --git a/src/main/java/org/traccar/api/ObjectMapperProvider.java b/src/main/java/org/traccar/api/ObjectMapperProvider.java new file mode 100644 index 000000000..f81b20917 --- /dev/null +++ b/src/main/java/org/traccar/api/ObjectMapperProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 - 2016 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. + * 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 com.fasterxml.jackson.databind.ObjectMapper; +import org.traccar.Context; + +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Provider; + +@Provider +public class ObjectMapperProvider implements ContextResolver<ObjectMapper> { + + @Override + public ObjectMapper getContext(Class<?> type) { + return Context.getObjectMapper(); + } + +} diff --git a/src/main/java/org/traccar/api/ResourceErrorHandler.java b/src/main/java/org/traccar/api/ResourceErrorHandler.java new file mode 100644 index 000000000..1d618b08d --- /dev/null +++ b/src/main/java/org/traccar/api/ResourceErrorHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 - 2016 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. + * 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 org.traccar.helper.Log; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +public class ResourceErrorHandler implements ExceptionMapper<Exception> { + + @Override + public Response toResponse(Exception e) { + if (e instanceof WebApplicationException) { + WebApplicationException exception = (WebApplicationException) e; + String message; + if (exception.getCause() != null) { + message = Log.exceptionStack(exception.getCause()); + } else { + message = Log.exceptionStack(exception); + } + return Response.fromResponse(exception.getResponse()).entity(message).build(); + } else { + return Response.status(Response.Status.BAD_REQUEST).entity(Log.exceptionStack(e)).build(); + } + } + +} diff --git a/src/main/java/org/traccar/api/SecurityRequestFilter.java b/src/main/java/org/traccar/api/SecurityRequestFilter.java new file mode 100644 index 000000000..33b6b37df --- /dev/null +++ b/src/main/java/org/traccar/api/SecurityRequestFilter.java @@ -0,0 +1,119 @@ +/* + * Copyright 2015 - 2016 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. + * 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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Context; +import org.traccar.Main; +import org.traccar.api.resource.SessionResource; +import org.traccar.database.StatisticsManager; +import org.traccar.helper.DataConverter; +import org.traccar.model.User; + +import javax.annotation.security.PermitAll; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.SecurityContext; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; + +public class SecurityRequestFilter implements ContainerRequestFilter { + + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityRequestFilter.class); + + public static final String AUTHORIZATION_HEADER = "Authorization"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String BASIC_REALM = "Basic realm=\"api\""; + public static final String X_REQUESTED_WITH = "X-Requested-With"; + public static final String XML_HTTP_REQUEST = "XMLHttpRequest"; + + public static String[] decodeBasicAuth(String auth) { + auth = auth.replaceFirst("[B|b]asic ", ""); + byte[] decodedBytes = DataConverter.parseBase64(auth); + if (decodedBytes != null && decodedBytes.length > 0) { + return new String(decodedBytes, StandardCharsets.US_ASCII).split(":", 2); + } + return null; + } + + @javax.ws.rs.core.Context + private HttpServletRequest request; + + @javax.ws.rs.core.Context + private ResourceInfo resourceInfo; + + @Override + public void filter(ContainerRequestContext requestContext) { + + if (requestContext.getMethod().equals("OPTIONS")) { + return; + } + + SecurityContext securityContext = null; + + try { + + String authHeader = requestContext.getHeaderString(AUTHORIZATION_HEADER); + if (authHeader != null) { + + try { + String[] auth = decodeBasicAuth(authHeader); + User user = Context.getPermissionsManager().login(auth[0], auth[1]); + if (user != null) { + Main.getInjector().getInstance(StatisticsManager.class).registerRequest(user.getId()); + securityContext = new UserSecurityContext(new UserPrincipal(user.getId())); + } + } catch (SQLException e) { + throw new WebApplicationException(e); + } + + } else if (request.getSession() != null) { + + Long userId = (Long) request.getSession().getAttribute(SessionResource.USER_ID_KEY); + if (userId != null) { + Context.getPermissionsManager().checkUserEnabled(userId); + Main.getInjector().getInstance(StatisticsManager.class).registerRequest(userId); + securityContext = new UserSecurityContext(new UserPrincipal(userId)); + } + + } + + } catch (SecurityException e) { + LOGGER.warn("Authentication error", e); + } + + if (securityContext != null) { + requestContext.setSecurityContext(securityContext); + } else { + Method method = resourceInfo.getResourceMethod(); + if (!method.isAnnotationPresent(PermitAll.class)) { + Response.ResponseBuilder responseBuilder = Response.status(Response.Status.UNAUTHORIZED); + if (!XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) { + responseBuilder.header(WWW_AUTHENTICATE, BASIC_REALM); + } + throw new WebApplicationException(responseBuilder.build()); + } + } + + } + +} diff --git a/src/main/java/org/traccar/api/SimpleObjectResource.java b/src/main/java/org/traccar/api/SimpleObjectResource.java new file mode 100644 index 000000000..a7fcae0e7 --- /dev/null +++ b/src/main/java/org/traccar/api/SimpleObjectResource.java @@ -0,0 +1,43 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 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.sql.SQLException; +import java.util.Collection; + +import javax.ws.rs.GET; +import javax.ws.rs.QueryParam; + +import org.traccar.Context; +import org.traccar.database.BaseObjectManager; +import org.traccar.model.BaseModel; + +public class SimpleObjectResource<T extends BaseModel> extends BaseObjectResource<T> { + + public SimpleObjectResource(Class<T> baseClass) { + super(baseClass); + } + + @GET + public Collection<T> get( + @QueryParam("all") boolean all, @QueryParam("userId") long userId) throws SQLException { + + BaseObjectManager<T> manager = Context.getManager(getBaseClass()); + return manager.getItems(getSimpleManagerItems(manager, all, userId)); + } + +} diff --git a/src/main/java/org/traccar/api/UserPrincipal.java b/src/main/java/org/traccar/api/UserPrincipal.java new file mode 100644 index 000000000..80e92c2dd --- /dev/null +++ b/src/main/java/org/traccar/api/UserPrincipal.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 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. + * 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.security.Principal; + +public class UserPrincipal implements Principal { + + private long userId; + + public UserPrincipal(long userId) { + this.userId = userId; + } + + public Long getUserId() { + return userId; + } + + @Override + public String getName() { + return null; + } + +} diff --git a/src/main/java/org/traccar/api/UserSecurityContext.java b/src/main/java/org/traccar/api/UserSecurityContext.java new file mode 100644 index 000000000..55c0621bc --- /dev/null +++ b/src/main/java/org/traccar/api/UserSecurityContext.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 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. + * 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 javax.ws.rs.core.SecurityContext; +import java.security.Principal; + +public class UserSecurityContext implements SecurityContext { + + private UserPrincipal principal; + + public UserSecurityContext(UserPrincipal principal) { + this.principal = principal; + } + + @Override + public Principal getUserPrincipal() { + return principal; + } + + @Override + public boolean isUserInRole(String role) { + return true; + } + + @Override + public boolean isSecure() { + return false; + } + + @Override + public String getAuthenticationScheme() { + return BASIC_AUTH; + } + +} diff --git a/src/main/java/org/traccar/api/resource/AttributeResource.java b/src/main/java/org/traccar/api/resource/AttributeResource.java new file mode 100644 index 000000000..de69d871c --- /dev/null +++ b/src/main/java/org/traccar/api/resource/AttributeResource.java @@ -0,0 +1,97 @@ +/* + * Copyright 2017 - 2019 Anton Tananaev (anton@traccar.org) + * Copyright 2017 - 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.resource; + +import java.sql.SQLException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.traccar.Context; +import org.traccar.api.ExtendedObjectResource; +import org.traccar.model.Attribute; +import org.traccar.model.Position; +import org.traccar.handler.ComputedAttributesHandler; + +@Path("attributes/computed") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class AttributeResource extends ExtendedObjectResource<Attribute> { + + public AttributeResource() { + super(Attribute.class); + } + + @POST + @Path("test") + public Response test(@QueryParam("deviceId") long deviceId, Attribute entity) { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + Position last = Context.getIdentityManager().getLastPosition(deviceId); + if (last != null) { + Object result = new ComputedAttributesHandler( + Context.getConfig(), + Context.getIdentityManager(), + Context.getAttributesManager()).computeAttribute(entity, last); + if (result != null) { + switch (entity.getType()) { + case "number": + Number numberValue = (Number) result; + return Response.ok(numberValue).build(); + case "boolean": + Boolean booleanValue = (Boolean) result; + return Response.ok(booleanValue).build(); + default: + return Response.ok(result.toString()).build(); + } + } else { + return Response.noContent().build(); + } + } else { + throw new IllegalArgumentException("Device has no last position"); + } + } + + @POST + public Response add(Attribute entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + return super.add(entity); + } + + @Path("{id}") + @PUT + public Response update(Attribute entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + return super.update(entity); + } + + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + return super.remove(id); + } + +} diff --git a/src/main/java/org/traccar/api/resource/CalendarResource.java b/src/main/java/org/traccar/api/resource/CalendarResource.java new file mode 100644 index 000000000..9399c34a5 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/CalendarResource.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 2017 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.resource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.traccar.api.SimpleObjectResource; +import org.traccar.model.Calendar; + +@Path("calendars") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class CalendarResource extends SimpleObjectResource<Calendar> { + + public CalendarResource() { + super(Calendar.class); + } + +} diff --git a/src/main/java/org/traccar/api/resource/CommandResource.java b/src/main/java/org/traccar/api/resource/CommandResource.java new file mode 100644 index 000000000..703638701 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/CommandResource.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015 - 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2016 Gabor Somogyi (gabor.g.somogyi@gmail.com) + * Copyright 2017 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.resource; + +import org.traccar.Context; +import org.traccar.api.ExtendedObjectResource; +import org.traccar.database.CommandsManager; +import org.traccar.model.Command; +import org.traccar.model.Typed; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +@Path("commands") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class CommandResource extends ExtendedObjectResource<Command> { + + public CommandResource() { + super(Command.class); + } + + @GET + @Path("send") + public Collection<Command> get(@QueryParam("deviceId") long deviceId) throws SQLException { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + CommandsManager commandsManager = Context.getCommandsManager(); + Set<Long> result = new HashSet<>(commandsManager.getUserItems(getUserId())); + result.retainAll(commandsManager.getSupportedCommands(deviceId)); + return commandsManager.getItems(result); + } + + @POST + @Path("send") + public Response send(Command entity) throws Exception { + Context.getPermissionsManager().checkReadonly(getUserId()); + long deviceId = entity.getDeviceId(); + long id = entity.getId(); + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + if (id != 0) { + Context.getPermissionsManager().checkPermission(Command.class, getUserId(), id); + Context.getPermissionsManager().checkUserDeviceCommand(getUserId(), deviceId, id); + } else { + Context.getPermissionsManager().checkLimitCommands(getUserId()); + } + if (!Context.getCommandsManager().sendCommand(entity)) { + return Response.accepted(entity).build(); + } + return Response.ok(entity).build(); + } + + @GET + @Path("types") + public Collection<Typed> get(@QueryParam("deviceId") long deviceId, + @QueryParam("textChannel") boolean textChannel) { + if (deviceId != 0) { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + return Context.getCommandsManager().getCommandTypes(deviceId, textChannel); + } else { + return Context.getCommandsManager().getAllCommandTypes(); + } + } +} diff --git a/src/main/java/org/traccar/api/resource/DeviceResource.java b/src/main/java/org/traccar/api/resource/DeviceResource.java new file mode 100644 index 000000000..f9c9a139d --- /dev/null +++ b/src/main/java/org/traccar/api/resource/DeviceResource.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 - 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseObjectResource; +import org.traccar.database.DeviceManager; +import org.traccar.helper.LogAction; +import org.traccar.model.Device; +import org.traccar.model.DeviceAccumulators; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.sql.SQLException; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Path("devices") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DeviceResource extends BaseObjectResource<Device> { + + public DeviceResource() { + super(Device.class); + } + + @GET + public Collection<Device> get( + @QueryParam("all") boolean all, @QueryParam("userId") long userId, + @QueryParam("uniqueId") List<String> uniqueIds, + @QueryParam("id") List<Long> deviceIds) throws SQLException { + DeviceManager deviceManager = Context.getDeviceManager(); + Set<Long> result = null; + if (all) { + if (Context.getPermissionsManager().getUserAdmin(getUserId())) { + result = deviceManager.getAllItems(); + } else { + Context.getPermissionsManager().checkManager(getUserId()); + result = deviceManager.getManagedItems(getUserId()); + } + } else if (uniqueIds.isEmpty() && deviceIds.isEmpty()) { + if (userId == 0) { + userId = getUserId(); + } + Context.getPermissionsManager().checkUser(getUserId(), userId); + if (Context.getPermissionsManager().getUserAdmin(getUserId())) { + result = deviceManager.getAllUserItems(userId); + } else { + result = deviceManager.getUserItems(userId); + } + } else { + result = new HashSet<>(); + for (String uniqueId : uniqueIds) { + Device device = deviceManager.getByUniqueId(uniqueId); + Context.getPermissionsManager().checkDevice(getUserId(), device.getId()); + result.add(device.getId()); + } + for (Long deviceId : deviceIds) { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + result.add(deviceId); + } + } + return deviceManager.getItems(result); + } + + @Path("{id}/accumulators") + @PUT + public Response updateAccumulators(DeviceAccumulators entity) throws SQLException { + if (!Context.getPermissionsManager().getUserAdmin(getUserId())) { + Context.getPermissionsManager().checkManager(getUserId()); + Context.getPermissionsManager().checkPermission(Device.class, getUserId(), entity.getDeviceId()); + } + Context.getDeviceManager().resetDeviceAccumulators(entity); + LogAction.resetDeviceAccumulators(getUserId(), entity.getDeviceId()); + return Response.noContent().build(); + } + +} diff --git a/src/main/java/org/traccar/api/resource/DriverResource.java b/src/main/java/org/traccar/api/resource/DriverResource.java new file mode 100644 index 000000000..91aa54c5e --- /dev/null +++ b/src/main/java/org/traccar/api/resource/DriverResource.java @@ -0,0 +1,36 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 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.resource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.traccar.api.ExtendedObjectResource; +import org.traccar.model.Driver; + +@Path("drivers") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DriverResource extends ExtendedObjectResource<Driver> { + + public DriverResource() { + super(Driver.class); + } + +} diff --git a/src/main/java/org/traccar/api/resource/EventResource.java b/src/main/java/org/traccar/api/resource/EventResource.java new file mode 100644 index 000000000..e0ccf7020 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/EventResource.java @@ -0,0 +1,38 @@ +package org.traccar.api.resource; + +import java.sql.SQLException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.Event; +import org.traccar.model.Geofence; +import org.traccar.model.Maintenance; + +@Path("events") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) + +public class EventResource extends BaseResource { + + @Path("{id}") + @GET + public Event get(@PathParam("id") long id) throws SQLException { + Event event = Context.getDataManager().getObject(Event.class, id); + Context.getPermissionsManager().checkDevice(getUserId(), event.getDeviceId()); + if (event.getGeofenceId() != 0) { + Context.getPermissionsManager().checkPermission(Geofence.class, getUserId(), event.getGeofenceId()); + } + if (event.getMaintenanceId() != 0) { + Context.getPermissionsManager().checkPermission(Maintenance.class, getUserId(), event.getMaintenanceId()); + } + return event; + } + +} diff --git a/src/main/java/org/traccar/api/resource/GeofenceResource.java b/src/main/java/org/traccar/api/resource/GeofenceResource.java new file mode 100644 index 000000000..58f2c188c --- /dev/null +++ b/src/main/java/org/traccar/api/resource/GeofenceResource.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2017 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. + * 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.resource; + +import org.traccar.api.ExtendedObjectResource; +import org.traccar.model.Geofence; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("geofences") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class GeofenceResource extends ExtendedObjectResource<Geofence> { + + public GeofenceResource() { + super(Geofence.class); + } + +} diff --git a/src/main/java/org/traccar/api/resource/GroupResource.java b/src/main/java/org/traccar/api/resource/GroupResource.java new file mode 100644 index 000000000..fcea15d0a --- /dev/null +++ b/src/main/java/org/traccar/api/resource/GroupResource.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2017 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. + * 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.resource; + +import org.traccar.api.SimpleObjectResource; +import org.traccar.model.Group; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +@Path("groups") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class GroupResource extends SimpleObjectResource<Group> { + + public GroupResource() { + super(Group.class); + } + +} diff --git a/src/main/java/org/traccar/api/resource/MaintenanceResource.java b/src/main/java/org/traccar/api/resource/MaintenanceResource.java new file mode 100644 index 000000000..fa1b359ce --- /dev/null +++ b/src/main/java/org/traccar/api/resource/MaintenanceResource.java @@ -0,0 +1,36 @@ +/* + * 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.resource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.traccar.api.ExtendedObjectResource; +import org.traccar.model.Maintenance; + +@Path("maintenance") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class MaintenanceResource extends ExtendedObjectResource<Maintenance> { + + public MaintenanceResource() { + super(Maintenance.class); + } + +} diff --git a/src/main/java/org/traccar/api/resource/NotificationResource.java b/src/main/java/org/traccar/api/resource/NotificationResource.java new file mode 100644 index 000000000..9631a52b7 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/NotificationResource.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 - 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. + * 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.resource; + +import java.util.Collection; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.traccar.Context; +import org.traccar.api.ExtendedObjectResource; +import org.traccar.model.Event; +import org.traccar.model.Notification; +import org.traccar.model.Typed; +import org.traccar.notification.MessageException; + + +@Path("notifications") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class NotificationResource extends ExtendedObjectResource<Notification> { + + public NotificationResource() { + super(Notification.class); + } + + @GET + @Path("types") + public Collection<Typed> get() { + return Context.getNotificationManager().getAllNotificationTypes(); + } + + @GET + @Path("notificators") + public Collection<Typed> getNotificators() { + return Context.getNotificatorManager().getAllNotificatorTypes(); + } + + @POST + @Path("test") + public Response testMessage() throws MessageException, InterruptedException { + for (Typed method : Context.getNotificatorManager().getAllNotificatorTypes()) { + Context.getNotificatorManager() + .getNotificator(method.getType()).sendSync(getUserId(), new Event("test", 0), null); + } + return Response.noContent().build(); + } + + @POST + @Path("test/{notificator}") + public Response testMessage(@PathParam("notificator") String notificator) + throws MessageException, InterruptedException { + Context.getNotificatorManager().getNotificator(notificator).sendSync(getUserId(), new Event("test", 0), null); + return Response.noContent().build(); + } + +} diff --git a/src/main/java/org/traccar/api/resource/PermissionsResource.java b/src/main/java/org/traccar/api/resource/PermissionsResource.java new file mode 100644 index 000000000..b89d9d376 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/PermissionsResource.java @@ -0,0 +1,84 @@ +/* + * Copyright 2017 Anton Tananaev (anton@traccar.org) + * Copyright 2017 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.resource; + +import java.sql.SQLException; +import java.util.LinkedHashMap; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.LogAction; +import org.traccar.model.Device; +import org.traccar.model.Permission; +import org.traccar.model.User; + +@Path("permissions") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class PermissionsResource extends BaseResource { + + private void checkPermission(Permission permission, boolean link) { + if (!link && permission.getOwnerClass().equals(User.class) + && permission.getPropertyClass().equals(Device.class)) { + if (getUserId() != permission.getOwnerId()) { + Context.getPermissionsManager().checkUser(getUserId(), permission.getOwnerId()); + } else { + Context.getPermissionsManager().checkAdmin(getUserId()); + } + } else { + Context.getPermissionsManager().checkPermission( + permission.getOwnerClass(), getUserId(), permission.getOwnerId()); + } + Context.getPermissionsManager().checkPermission( + permission.getPropertyClass(), getUserId(), permission.getPropertyId()); + } + + @POST + public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException { + Context.getPermissionsManager().checkReadonly(getUserId()); + Permission permission = new Permission(entity); + checkPermission(permission, true); + Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId(), true); + LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId()); + Context.getPermissionsManager().refreshPermissions(permission); + return Response.noContent().build(); + } + + @DELETE + public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException { + Context.getPermissionsManager().checkReadonly(getUserId()); + Permission permission = new Permission(entity); + checkPermission(permission, false); + Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId(), false); + LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(), + permission.getPropertyClass(), permission.getPropertyId()); + Context.getPermissionsManager().refreshPermissions(permission); + return Response.noContent().build(); + } + +} diff --git a/src/main/java/org/traccar/api/resource/PositionResource.java b/src/main/java/org/traccar/api/resource/PositionResource.java new file mode 100644 index 000000000..c031b842f --- /dev/null +++ b/src/main/java/org/traccar/api/resource/PositionResource.java @@ -0,0 +1,96 @@ +/* + * Copyright 2015 - 2016 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.DateUtil; +import org.traccar.model.Position; +import org.traccar.web.CsvBuilder; +import org.traccar.web.GpxBuilder; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Path("positions") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class PositionResource extends BaseResource { + + public static final String TEXT_CSV = "text/csv"; + public static final String CONTENT_DISPOSITION_VALUE_CSV = "attachment; filename=positions.csv"; + public static final String GPX = "application/gpx+xml"; + public static final String CONTENT_DISPOSITION_VALUE_GPX = "attachment; filename=positions.gpx"; + + @GET + public Collection<Position> getJson( + @QueryParam("deviceId") long deviceId, @QueryParam("id") List<Long> positionIds, + @QueryParam("from") String from, @QueryParam("to") String to) + throws SQLException { + if (!positionIds.isEmpty()) { + ArrayList<Position> positions = new ArrayList<>(); + for (Long positionId : positionIds) { + Position position = Context.getDataManager().getObject(Position.class, positionId); + Context.getPermissionsManager().checkDevice(getUserId(), position.getDeviceId()); + positions.add(position); + } + return positions; + } else if (deviceId == 0) { + return Context.getDeviceManager().getInitialState(getUserId()); + } else { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + return Context.getDataManager().getPositions( + deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + } + + @GET + @Produces(TEXT_CSV) + public Response getCsv( + @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to) + throws SQLException { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + CsvBuilder csv = new CsvBuilder(); + csv.addHeaderLine(new Position()); + csv.addArray(Context.getDataManager().getPositions( + deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to))); + return Response.ok(csv.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_CSV).build(); + } + + @GET + @Produces(GPX) + public Response getGpx( + @QueryParam("deviceId") long deviceId, @QueryParam("from") String from, @QueryParam("to") String to) + throws SQLException { + Context.getPermissionsManager().checkDevice(getUserId(), deviceId); + GpxBuilder gpx = new GpxBuilder(Context.getIdentityManager().getById(deviceId).getName()); + gpx.addPositions(Context.getDataManager().getPositions( + deviceId, DateUtil.parseDate(from), DateUtil.parseDate(to))); + return Response.ok(gpx.build()).header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_GPX).build(); + } + +} diff --git a/src/main/java/org/traccar/api/resource/ReportResource.java b/src/main/java/org/traccar/api/resource/ReportResource.java new file mode 100644 index 000000000..d371cf987 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/ReportResource.java @@ -0,0 +1,210 @@ +/* + * Copyright 2016 - 2018 Anton Tananaev (anton@traccar.org) + * Copyright 2016 - 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.resource; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; + +import javax.activation.DataHandler; +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; +import javax.mail.util.ByteArrayDataSource; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.DateUtil; +import org.traccar.model.Event; +import org.traccar.model.Position; +import org.traccar.reports.Events; +import org.traccar.reports.Summary; +import org.traccar.reports.Trips; +import org.traccar.reports.model.StopReport; +import org.traccar.reports.model.SummaryReport; +import org.traccar.reports.model.TripReport; +import org.traccar.reports.Route; +import org.traccar.reports.Stops; + +@Path("reports") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ReportResource extends BaseResource { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReportResource.class); + + private static final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private static final String CONTENT_DISPOSITION_VALUE_XLSX = "attachment; filename=report.xlsx"; + + private interface ReportExecutor { + void execute(ByteArrayOutputStream stream) throws SQLException, IOException; + } + + private Response executeReport( + long userId, boolean mail, ReportExecutor executor) throws SQLException, IOException { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + if (mail) { + new Thread(() -> { + try { + executor.execute(stream); + + MimeBodyPart attachment = new MimeBodyPart(); + + attachment.setFileName("report.xlsx"); + attachment.setDataHandler(new DataHandler(new ByteArrayDataSource( + stream.toByteArray(), "application/octet-stream"))); + + Context.getMailManager().sendMessage( + userId, "Report", "The report is in the attachment.", attachment); + } catch (SQLException | IOException | MessagingException e) { + LOGGER.warn("Report failed", e); + } + }).start(); + return Response.noContent().build(); + } else { + executor.execute(stream); + return Response.ok(stream.toByteArray()) + .header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION_VALUE_XLSX).build(); + } + } + + @Path("route") + @GET + public Collection<Position> getRoute( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Route.getObjects(getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + + @Path("route") + @GET + @Produces(XLSX) + public Response getRouteExcel( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + throws SQLException, IOException { + return executeReport(getUserId(), mail, stream -> { + Route.getExcel(stream, getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + }); + } + + @Path("events") + @GET + public Collection<Event> getEvents( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("type") final List<String> types, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Events.getObjects(getUserId(), deviceIds, groupIds, types, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + + @Path("events") + @GET + @Produces(XLSX) + public Response getEventsExcel( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("type") final List<String> types, + @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + throws SQLException, IOException { + return executeReport(getUserId(), mail, stream -> { + Events.getExcel(stream, getUserId(), deviceIds, groupIds, types, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + }); + } + + @Path("summary") + @GET + public Collection<SummaryReport> getSummary( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Summary.getObjects(getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + + @Path("summary") + @GET + @Produces(XLSX) + public Response getSummaryExcel( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + throws SQLException, IOException { + return executeReport(getUserId(), mail, stream -> { + Summary.getExcel(stream, getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + }); + } + + @Path("trips") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Collection<TripReport> getTrips( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Trips.getObjects(getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + + @Path("trips") + @GET + @Produces(XLSX) + public Response getTripsExcel( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + throws SQLException, IOException { + return executeReport(getUserId(), mail, stream -> { + Trips.getExcel(stream, getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + }); + } + + @Path("stops") + @GET + @Produces(MediaType.APPLICATION_JSON) + public Collection<StopReport> getStops( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + return Stops.getObjects(getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + + @Path("stops") + @GET + @Produces(XLSX) + public Response getStopsExcel( + @QueryParam("deviceId") final List<Long> deviceIds, @QueryParam("groupId") final List<Long> groupIds, + @QueryParam("from") String from, @QueryParam("to") String to, @QueryParam("mail") boolean mail) + throws SQLException, IOException { + return executeReport(getUserId(), mail, stream -> { + Stops.getExcel(stream, getUserId(), deviceIds, groupIds, + DateUtil.parseDate(from), DateUtil.parseDate(to)); + }); + } + +} diff --git a/src/main/java/org/traccar/api/resource/ServerResource.java b/src/main/java/org/traccar/api/resource/ServerResource.java new file mode 100644 index 000000000..e7cad2a0c --- /dev/null +++ b/src/main/java/org/traccar/api/resource/ServerResource.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 - 2017 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.LogAction; +import org.traccar.model.Server; + +import javax.annotation.security.PermitAll; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.SQLException; + +@Path("server") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class ServerResource extends BaseResource { + + @PermitAll + @GET + public Server get() throws SQLException { + return Context.getPermissionsManager().getServer(); + } + + @PUT + public Response update(Server entity) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + Context.getPermissionsManager().updateServer(entity); + LogAction.edit(getUserId(), entity); + return Response.ok(entity).build(); + } + + @Path("geocode") + @GET + public String geocode(@QueryParam("latitude") double latitude, @QueryParam("longitude") double longitude) { + if (Context.getGeocoder() != null) { + return Context.getGeocoder().getAddress(latitude, longitude, null); + } else { + throw new RuntimeException("Reverse geocoding is not enabled"); + } + } + +} diff --git a/src/main/java/org/traccar/api/resource/SessionResource.java b/src/main/java/org/traccar/api/resource/SessionResource.java new file mode 100644 index 000000000..fd331c766 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/SessionResource.java @@ -0,0 +1,120 @@ +/* + * Copyright 2015 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.DataConverter; +import org.traccar.helper.LogAction; +import org.traccar.model.User; + +import javax.annotation.security.PermitAll; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.FormParam; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; + +@Path("session") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_FORM_URLENCODED) +public class SessionResource extends BaseResource { + + public static final String USER_ID_KEY = "userId"; + public static final String USER_COOKIE_KEY = "user"; + public static final String PASS_COOKIE_KEY = "password"; + + @javax.ws.rs.core.Context + private HttpServletRequest request; + + @PermitAll + @GET + public User get(@QueryParam("token") String token) throws SQLException, UnsupportedEncodingException { + Long userId = (Long) request.getSession().getAttribute(USER_ID_KEY); + if (userId == null) { + Cookie[] cookies = request.getCookies(); + String email = null, password = null; + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(USER_COOKIE_KEY)) { + byte[] emailBytes = DataConverter.parseBase64( + URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name())); + email = new String(emailBytes, StandardCharsets.UTF_8); + } else if (cookie.getName().equals(PASS_COOKIE_KEY)) { + byte[] passwordBytes = DataConverter.parseBase64( + URLDecoder.decode(cookie.getValue(), StandardCharsets.US_ASCII.name())); + password = new String(passwordBytes, StandardCharsets.UTF_8); + } + } + } + if (email != null && password != null) { + User user = Context.getPermissionsManager().login(email, password); + if (user != null) { + userId = user.getId(); + request.getSession().setAttribute(USER_ID_KEY, userId); + } + } else if (token != null) { + User user = Context.getUsersManager().getUserByToken(token); + if (user != null) { + userId = user.getId(); + request.getSession().setAttribute(USER_ID_KEY, userId); + } + } + } + + if (userId != null) { + Context.getPermissionsManager().checkUserEnabled(userId); + return Context.getPermissionsManager().getUser(userId); + } else { + throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()); + } + } + + @PermitAll + @POST + public User add( + @FormParam("email") String email, @FormParam("password") String password) throws SQLException { + User user = Context.getPermissionsManager().login(email, password); + if (user != null) { + request.getSession().setAttribute(USER_ID_KEY, user.getId()); + LogAction.login(user.getId()); + return user; + } else { + throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build()); + } + } + + @DELETE + public Response remove() { + LogAction.logout(getUserId()); + request.getSession().removeAttribute(USER_ID_KEY); + return Response.noContent().build(); + } + +} diff --git a/src/main/java/org/traccar/api/resource/StatisticsResource.java b/src/main/java/org/traccar/api/resource/StatisticsResource.java new file mode 100644 index 000000000..e801d4ff3 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/StatisticsResource.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.helper.DateUtil; +import org.traccar.model.Statistics; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import java.sql.SQLException; +import java.util.Collection; + +@Path("statistics") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class StatisticsResource extends BaseResource { + + @GET + public Collection<Statistics> get( + @QueryParam("from") String from, @QueryParam("to") String to) throws SQLException { + Context.getPermissionsManager().checkAdmin(getUserId()); + return Context.getDataManager().getStatistics(DateUtil.parseDate(from), DateUtil.parseDate(to)); + } + +} diff --git a/src/main/java/org/traccar/api/resource/UserResource.java b/src/main/java/org/traccar/api/resource/UserResource.java new file mode 100644 index 000000000..0b42d8d92 --- /dev/null +++ b/src/main/java/org/traccar/api/resource/UserResource.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015 - 2017 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. + * 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.resource; + +import org.traccar.Context; +import org.traccar.api.BaseObjectResource; +import org.traccar.database.UsersManager; +import org.traccar.helper.LogAction; +import org.traccar.model.ManagedUser; +import org.traccar.model.User; + +import javax.annotation.security.PermitAll; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.sql.SQLException; +import java.util.Collection; +import java.util.Date; +import java.util.Set; + +@Path("users") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserResource extends BaseObjectResource<User> { + + public UserResource() { + super(User.class); + } + + @GET + public Collection<User> get(@QueryParam("userId") long userId) throws SQLException { + UsersManager usersManager = Context.getUsersManager(); + Set<Long> result = null; + if (Context.getPermissionsManager().getUserAdmin(getUserId())) { + if (userId != 0) { + result = usersManager.getUserItems(userId); + } else { + result = usersManager.getAllItems(); + } + } else if (Context.getPermissionsManager().getUserManager(getUserId())) { + result = usersManager.getManagedItems(getUserId()); + } else { + throw new SecurityException("Admin or manager access required"); + } + return usersManager.getItems(result); + } + + @Override + @PermitAll + @POST + public Response add(User entity) throws SQLException { + if (!Context.getPermissionsManager().getUserAdmin(getUserId())) { + Context.getPermissionsManager().checkUserUpdate(getUserId(), new User(), entity); + if (Context.getPermissionsManager().getUserManager(getUserId())) { + Context.getPermissionsManager().checkUserLimit(getUserId()); + } else { + Context.getPermissionsManager().checkRegistration(getUserId()); + entity.setDeviceLimit(Context.getConfig().getInteger("users.defaultDeviceLimit", -1)); + int expirationDays = Context.getConfig().getInteger("users.defaultExpirationDays"); + if (expirationDays > 0) { + entity.setExpirationTime( + new Date(System.currentTimeMillis() + (long) expirationDays * 24 * 3600 * 1000)); + } + } + } + Context.getUsersManager().addItem(entity); + LogAction.create(getUserId(), entity); + if (Context.getPermissionsManager().getUserManager(getUserId())) { + Context.getDataManager().linkObject(User.class, getUserId(), ManagedUser.class, entity.getId(), true); + LogAction.link(getUserId(), User.class, getUserId(), ManagedUser.class, entity.getId()); + } + Context.getUsersManager().refreshUserItems(); + return Response.ok(entity).build(); + } + +} |