aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/api
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
committerAnton Tananaev <anton.tananaev@gmail.com>2019-03-31 22:35:39 -0700
commit59416923dcb3a756eaf532cc4259f2f6625c0762 (patch)
tree9082dae6616deac8fda432b7bfd80e4a52b6d9dc /src/main/java/org/traccar/api
parent79a129dd6327d932133d6b9a50190d3f4927bff9 (diff)
downloadtraccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.gz
traccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.tar.bz2
traccar-server-59416923dcb3a756eaf532cc4259f2f6625c0762.zip
Convert project to gradle
Diffstat (limited to 'src/main/java/org/traccar/api')
-rw-r--r--src/main/java/org/traccar/api/AsyncSocket.java96
-rw-r--r--src/main/java/org/traccar/api/AsyncSocketServlet.java46
-rw-r--r--src/main/java/org/traccar/api/BaseObjectResource.java176
-rw-r--r--src/main/java/org/traccar/api/BaseResource.java32
-rw-r--r--src/main/java/org/traccar/api/CorsResponseFilter.java58
-rw-r--r--src/main/java/org/traccar/api/ExtendedObjectResource.java62
-rw-r--r--src/main/java/org/traccar/api/MediaFilter.java92
-rw-r--r--src/main/java/org/traccar/api/ObjectMapperProvider.java32
-rw-r--r--src/main/java/org/traccar/api/ResourceErrorHandler.java42
-rw-r--r--src/main/java/org/traccar/api/SecurityRequestFilter.java119
-rw-r--r--src/main/java/org/traccar/api/SimpleObjectResource.java43
-rw-r--r--src/main/java/org/traccar/api/UserPrincipal.java37
-rw-r--r--src/main/java/org/traccar/api/UserSecurityContext.java49
-rw-r--r--src/main/java/org/traccar/api/resource/AttributeResource.java97
-rw-r--r--src/main/java/org/traccar/api/resource/CalendarResource.java36
-rw-r--r--src/main/java/org/traccar/api/resource/CommandResource.java89
-rw-r--r--src/main/java/org/traccar/api/resource/DeviceResource.java100
-rw-r--r--src/main/java/org/traccar/api/resource/DriverResource.java36
-rw-r--r--src/main/java/org/traccar/api/resource/EventResource.java38
-rw-r--r--src/main/java/org/traccar/api/resource/GeofenceResource.java35
-rw-r--r--src/main/java/org/traccar/api/resource/GroupResource.java35
-rw-r--r--src/main/java/org/traccar/api/resource/MaintenanceResource.java36
-rw-r--r--src/main/java/org/traccar/api/resource/NotificationResource.java76
-rw-r--r--src/main/java/org/traccar/api/resource/PermissionsResource.java84
-rw-r--r--src/main/java/org/traccar/api/resource/PositionResource.java96
-rw-r--r--src/main/java/org/traccar/api/resource/ReportResource.java210
-rw-r--r--src/main/java/org/traccar/api/resource/ServerResource.java63
-rw-r--r--src/main/java/org/traccar/api/resource/SessionResource.java120
-rw-r--r--src/main/java/org/traccar/api/resource/StatisticsResource.java44
-rw-r--r--src/main/java/org/traccar/api/resource/UserResource.java94
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();
+ }
+
+}