diff options
-rw-r--r-- | pom.xml | 15 | ||||
-rw-r--r-- | src/org/traccar/api/BaseResource.java | 19 | ||||
-rw-r--r-- | src/org/traccar/api/CorsResponseFilter.java | 53 | ||||
-rw-r--r-- | src/org/traccar/api/SecurityRequestFilter.java | 61 | ||||
-rw-r--r-- | src/org/traccar/api/UserPrincipal.java | 39 | ||||
-rw-r--r-- | src/org/traccar/api/UserSecurityContext.java | 49 | ||||
-rw-r--r-- | src/org/traccar/api/resource/DeviceResource.java | 83 | ||||
-rw-r--r-- | src/org/traccar/api/resource/UserResource.java | 83 | ||||
-rw-r--r-- | src/org/traccar/database/DataManager.java | 15 | ||||
-rw-r--r-- | src/org/traccar/database/PermissionsManager.java | 8 | ||||
-rw-r--r-- | src/org/traccar/helper/Authorization.java | 49 | ||||
-rw-r--r-- | src/org/traccar/web/BaseServlet.java | 53 | ||||
-rw-r--r-- | src/org/traccar/web/CommandServlet.java | 9 | ||||
-rw-r--r-- | src/org/traccar/web/JsonConverter.java | 23 | ||||
-rw-r--r-- | src/org/traccar/web/MainServlet.java | 8 | ||||
-rw-r--r-- | src/org/traccar/web/WebServer.java | 23 | ||||
-rw-r--r-- | test/org/traccar/web/JsonConverterTest.java | 2 |
17 files changed, 489 insertions, 103 deletions
@@ -1,5 +1,5 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.traccar</groupId> @@ -12,6 +12,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jetty.version>9.2.14.v20151106</jetty.version> <!-- Jetty 9.3+ requires Java 8 --> + <jersey.version>2.22.1</jersey.version> </properties> <dependencies> @@ -91,6 +92,16 @@ <artifactId>jetty-jndi</artifactId> <version>${jetty.version}</version> </dependency> + <dependency> + <groupId>org.glassfish.jersey.containers</groupId> + <artifactId>jersey-container-servlet</artifactId> + <version>${jersey.version}</version> + </dependency> + <dependency> + <groupId>org.glassfish.jersey.media</groupId> + <artifactId>jersey-media-moxy</artifactId> + <version>${jersey.version}</version> + </dependency> </dependencies> <build> @@ -183,7 +194,7 @@ <artifactId>maven-project-info-reports-plugin</artifactId> <version>2.8.1</version> <configuration> - <dependencyLocationsEnabled>false</dependencyLocationsEnabled> + <dependencyLocationsEnabled>false</dependencyLocationsEnabled> </configuration> </plugin> <plugin> diff --git a/src/org/traccar/api/BaseResource.java b/src/org/traccar/api/BaseResource.java new file mode 100644 index 000000000..5a05c6732 --- /dev/null +++ b/src/org/traccar/api/BaseResource.java @@ -0,0 +1,19 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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; + +public class BaseResource { +} diff --git a/src/org/traccar/api/CorsResponseFilter.java b/src/org/traccar/api/CorsResponseFilter.java new file mode 100644 index 000000000..8aab5ad68 --- /dev/null +++ b/src/org/traccar/api/CorsResponseFilter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.api; + +import java.io.IOException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; + +public class CorsResponseFilter implements ContainerResponseFilter { + + public static final String ACCESS_CONTROL_ALLOW_ORIGIN_KEY = "Access-Control-Allow-Origin"; + public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*"; + + public static final String ACCESS_CONTROL_ALLOW_HEADERS_KEY = "Access-Control-Allow-Headers"; + public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "origin, content-type, accept, authorization"; + + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_KEY = "Access-Control-Allow-Credentials"; + public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true"; + + public static final String ACCESS_CONTROL_ALLOW_METHODS_KEY = "Access-Control-Allow-Methods"; + public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE"; + + @Override + public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException { + if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN_KEY)) { + response.getHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN_KEY, ACCESS_CONTROL_ALLOW_ORIGIN_VALUE); + } + if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS_KEY)) { + response.getHeaders().add(ACCESS_CONTROL_ALLOW_HEADERS_KEY, ACCESS_CONTROL_ALLOW_HEADERS_VALUE); + } + if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_CREDENTIALS_KEY)) { + response.getHeaders().add(ACCESS_CONTROL_ALLOW_CREDENTIALS_KEY, ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE); + } + if (!response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_METHODS_KEY)) { + response.getHeaders().add(ACCESS_CONTROL_ALLOW_METHODS_KEY, ACCESS_CONTROL_ALLOW_METHODS_VALUE); + } + } + +} diff --git a/src/org/traccar/api/SecurityRequestFilter.java b/src/org/traccar/api/SecurityRequestFilter.java new file mode 100644 index 000000000..63295a8b7 --- /dev/null +++ b/src/org/traccar/api/SecurityRequestFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.Context; +import org.traccar.model.User; + +import java.nio.charset.Charset; +import java.sql.SQLException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.core.Response; +import javax.xml.bind.DatatypeConverter; + +public class SecurityRequestFilter implements ContainerRequestFilter { + + 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 String[] decodeBasicAuth(String auth) { + auth = auth.replaceFirst("[B|b]asic ", ""); + byte[] decodedBytes = DatatypeConverter.parseBase64Binary(auth); + if (decodedBytes != null && decodedBytes.length > 0) { + return new String(decodedBytes, Charset.defaultCharset()).split(":", 2); + } + return null; + } + + @Override + public void filter(ContainerRequestContext requestContext) { + try { + String[] auth = decodeBasicAuth(requestContext.getHeaderString(AUTHORIZATION_HEADER)); + User user = Context.getDataManager().login(auth[0], auth[1]); + if (user != null) { + requestContext.setSecurityContext( + new UserSecurityContext(new UserPrincipal(user.getId(), user.getName()))); + } else { + throw new WebApplicationException( + Response.status(Response.Status.UNAUTHORIZED).header(WWW_AUTHENTICATE, BASIC_REALM).build()); + } + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + +} diff --git a/src/org/traccar/api/UserPrincipal.java b/src/org/traccar/api/UserPrincipal.java new file mode 100644 index 000000000..d858b6f47 --- /dev/null +++ b/src/org/traccar/api/UserPrincipal.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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 String name; + private long userId; + + public UserPrincipal(long userId, String name) { + this.userId = userId; + this.name = name; + } + + public Long getUserId() { + return userId; + } + + @Override + public String getName() { + return name; + } + +} diff --git a/src/org/traccar/api/UserSecurityContext.java b/src/org/traccar/api/UserSecurityContext.java new file mode 100644 index 000000000..127aee4b3 --- /dev/null +++ b/src/org/traccar/api/UserSecurityContext.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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; +import javax.ws.rs.core.SecurityContext; + +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/org/traccar/api/resource/DeviceResource.java b/src/org/traccar/api/resource/DeviceResource.java new file mode 100644 index 000000000..00b77e16c --- /dev/null +++ b/src/org/traccar/api/resource/DeviceResource.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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 java.sql.SQLException; +import java.util.Collection; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +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.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.traccar.model.Device; + +@Path("devices") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class DeviceResource extends BaseResource { + + @GET + public Collection<Device> get() { + try { + return Context.getDataManager().getAllDevices(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @POST + public Response add(Device entity) { + try { + Context.getDataManager().addDevice(entity); + return Response.ok(entity).build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @Path("{id}") + @PUT + public Response update(@PathParam("id") long id, Device entity) { + try { + entity.setId(id); + Context.getDataManager().updateDevice(entity); + return Response.ok(entity).build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) { + try { + Context.getDataManager().removeDevice(id); + return Response.noContent().build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + +} diff --git a/src/org/traccar/api/resource/UserResource.java b/src/org/traccar/api/resource/UserResource.java new file mode 100644 index 000000000..43bef891f --- /dev/null +++ b/src/org/traccar/api/resource/UserResource.java @@ -0,0 +1,83 @@ +/* + * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) + * + * 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.Collection; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +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.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.traccar.Context; +import org.traccar.api.BaseResource; +import org.traccar.model.User; + +@Path("users") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class UserResource extends BaseResource { + + @GET + public Collection<User> get() { + try { + return Context.getDataManager().getUsers(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @POST + public Response add(User entity) { + try { + Context.getDataManager().addUser(entity); + return Response.ok(entity).build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @Path("{id}") + @PUT + public Response update(@PathParam("id") long id, User entity) { + try { + entity.setId(id); + Context.getDataManager().updateUser(entity); + return Response.ok(entity).build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + + @Path("{id}") + @DELETE + public Response remove(@PathParam("id") long id) { + try { + Context.getDataManager().removeUser(id); + return Response.noContent().build(); + } catch (SQLException e) { + throw new WebApplicationException(e); + } + } + +} diff --git a/src/org/traccar/database/DataManager.java b/src/org/traccar/database/DataManager.java index da87c6c27..978f08ef3 100644 --- a/src/org/traccar/database/DataManager.java +++ b/src/org/traccar/database/DataManager.java @@ -277,12 +277,19 @@ public class DataManager implements IdentityManager { } } + @Deprecated public void removeUser(User user) throws SQLException { QueryBuilder.create(dataSource, getQuery("database.deleteUser")) .setObject(user) .executeUpdate(); } + public void removeUser(long userId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.deleteUser")) + .setLong("id", userId) + .executeUpdate(); + } + public Collection<Permission> getPermissions() throws SQLException { return QueryBuilder.create(dataSource, getQuery("database.getPermissionsAll")) .executeQuery(new Permission()); @@ -317,6 +324,7 @@ public class DataManager implements IdentityManager { .executeUpdate(); } + @Deprecated public void removeDevice(Device device) throws SQLException { QueryBuilder.create(dataSource, getQuery("database.deleteDevice")) .setObject(device) @@ -324,6 +332,13 @@ public class DataManager implements IdentityManager { AsyncServlet.sessionRefreshDevice(device.getId()); } + public void removeDevice(long deviceId) throws SQLException { + QueryBuilder.create(dataSource, getQuery("database.deleteDevice")) + .setLong("id", deviceId) + .executeUpdate(); + AsyncServlet.sessionRefreshDevice(deviceId); + } + public void linkDevice(long userId, long deviceId) throws SQLException { QueryBuilder.create(dataSource, getQuery("database.linkDevice")) .setLong("userId", userId) diff --git a/src/org/traccar/database/PermissionsManager.java b/src/org/traccar/database/PermissionsManager.java index a38a29c32..0a43f4ff4 100644 --- a/src/org/traccar/database/PermissionsManager.java +++ b/src/org/traccar/database/PermissionsManager.java @@ -22,6 +22,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import org.traccar.helper.Log; +import org.traccar.model.Device; import org.traccar.model.Permission; import org.traccar.model.User; @@ -82,4 +83,11 @@ public class PermissionsManager { } } + public <T> void check(Class<T> clazz, long userId, long entityId) throws SecurityException { + if (clazz.equals(User.class)) { + checkUser(userId, entityId); + } else if (clazz.equals(Device.class)) { + checkDevice(userId, entityId); + } + } } diff --git a/src/org/traccar/helper/Authorization.java b/src/org/traccar/helper/Authorization.java deleted file mode 100644 index d0877630d..000000000 --- a/src/org/traccar/helper/Authorization.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 Anton Tananaev (anton.tananaev@gmail.com) - * - * 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.helper; - -import java.util.HashMap; -import java.util.Map; -import java.util.StringTokenizer; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.handler.codec.base64.Base64; -import org.jboss.netty.util.CharsetUtil; - -public final class Authorization { - - private Authorization() { - } - - public static final String AUTHORIAZTION_SCHEME_VALUE = "Basic"; - public static final String REGEX = AUTHORIAZTION_SCHEME_VALUE + " "; - public static final String REPLACEMENT = ""; - public static final String TOKENIZER = ":"; - public static final String USERNAME = "username"; - public static final String PASSWORD = "password"; - public static final String WWW_AUTHENTICATE_VALUE = "Basic realm=\"api\""; - - public static Map<String, String> parse(String authorization) { - Map<String, String> authMap = new HashMap<>(); - final String encodedUsernameAndPassword = authorization.replaceFirst(REGEX, REPLACEMENT); - ChannelBuffer buffer = ChannelBuffers.copiedBuffer(encodedUsernameAndPassword, CharsetUtil.UTF_8); - String usernameAndPassword = Base64.decode(buffer).toString(CharsetUtil.UTF_8); - final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, TOKENIZER); - authMap.put(USERNAME, tokenizer.nextToken()); - authMap.put(PASSWORD, tokenizer.nextToken()); - return authMap; - } -} diff --git a/src/org/traccar/web/BaseServlet.java b/src/org/traccar/web/BaseServlet.java index 283edf1e5..d215c62d0 100644 --- a/src/org/traccar/web/BaseServlet.java +++ b/src/org/traccar/web/BaseServlet.java @@ -19,9 +19,10 @@ import org.traccar.helper.Log; import java.io.IOException; import java.io.Writer; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.AccessControlException; import java.util.Collection; -import java.util.Map; import javax.json.Json; import javax.json.JsonObjectBuilder; import javax.json.JsonStructure; @@ -32,20 +33,14 @@ import javax.servlet.http.HttpServletResponse; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.util.CharsetUtil; import org.traccar.Context; -import org.traccar.helper.Authorization; -import org.traccar.model.User; public abstract class BaseServlet extends HttpServlet { - public static final String USER_KEY = "user"; + public static final String USER_ID_KEY = "user"; public static final String ALLOW_ORIGIN_VALUE = "*"; public static final String ALLOW_HEADERS_VALUE = "Origin, X-Requested-With, Content-Type, Accept"; public static final String ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE"; public static final String APPLICATION_JSON = "application/json"; - public static final String GET = "GET"; - public static final String POST = "POST"; - public static final String PUT = "PUT"; - public static final String DELETE = "DELETE"; @Override protected final void service( @@ -61,7 +56,8 @@ public abstract class BaseServlet extends HttpServlet { if (allowed == null) { resp.setHeader(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW_ORIGIN_VALUE); } else if (allowed.contains(origin)) { - resp.setHeader(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, origin); + String originSafe = URLEncoder.encode(origin, StandardCharsets.UTF_8.displayName()); + resp.setHeader(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, originSafe); } if (!handle(getCommand(req), req, resp)) { @@ -70,7 +66,6 @@ public abstract class BaseServlet extends HttpServlet { } catch (Exception error) { if (error instanceof AccessControlException) { resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - resp.addHeader(HttpHeaders.Names.WWW_AUTHENTICATE, Authorization.WWW_AUTHENTICATE_VALUE); } else if (error instanceof SecurityException) { resp.setStatus(HttpServletResponse.SC_FORBIDDEN); } @@ -82,21 +77,11 @@ public abstract class BaseServlet extends HttpServlet { String command, HttpServletRequest req, HttpServletResponse resp) throws Exception; public long getUserId(HttpServletRequest req) throws Exception { - String authorization = req.getHeader(HttpHeaders.Names.AUTHORIZATION); - if (authorization != null && !authorization.isEmpty()) { - Map<String, String> authMap = Authorization.parse(authorization); - String username = authMap.get(Authorization.USERNAME); - String password = authMap.get(Authorization.PASSWORD); - User user = Context.getDataManager().login(username, password); - if (user != null) { - return user.getId(); - } - } - Long userId = (Long) req.getSession().getAttribute(USER_KEY); - if (userId == null) { - throw new AccessControlException("User not logged in"); + Object userId = req.getSession().getAttribute(USER_ID_KEY); + if (userId != null) { + return (Long) userId; } - return userId; + throw new AccessControlException("User not logged in"); } public void sendResponse(Writer writer, boolean success) throws IOException { @@ -129,26 +114,12 @@ public abstract class BaseServlet extends HttpServlet { writer.write(result.build().toString()); } - private String getCommand(HttpServletRequest req) { + protected String getCommand(HttpServletRequest req) { String command = req.getPathInfo(); if (command == null) { - switch (req.getMethod()) { - case GET: - command = "/get"; - break; - case POST: - command = "/add"; - break; - case PUT: - command = "/update"; - break; - case DELETE: - command = "/remove"; - break; - default: - command = ""; - } + command = ""; } return command; } + } diff --git a/src/org/traccar/web/CommandServlet.java b/src/org/traccar/web/CommandServlet.java index 67bca2d57..d307913df 100644 --- a/src/org/traccar/web/CommandServlet.java +++ b/src/org/traccar/web/CommandServlet.java @@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletResponse; import org.traccar.Context; import org.traccar.database.ActiveDevice; import org.traccar.model.Command; +import org.traccar.model.Device; public class CommandServlet extends BaseServlet { @@ -49,19 +50,17 @@ public class CommandServlet extends BaseServlet { } private void send(HttpServletRequest req, HttpServletResponse resp) throws Exception { - - Command command = JsonConverter.objectFromJson(req.getReader(), new Command()); - Context.getPermissionsManager().checkDevice(getUserId(req), command.getDeviceId()); + Command command = JsonConverter.objectFromJson(req.getReader(), Command.class); + Context.getPermissionsManager().check(Device.class, getUserId(req), command.getDeviceId()); getActiveDevice(command.getDeviceId()).sendCommand(command); sendResponse(resp.getWriter(), true); } private void raw(HttpServletRequest req, HttpServletResponse resp) throws Exception { - JsonObject json = Json.createReader(req.getReader()).readObject(); long deviceId = json.getJsonNumber("deviceId").longValue(); String command = json.getString("command"); - Context.getPermissionsManager().checkDevice(getUserId(req), deviceId); + Context.getPermissionsManager().check(Device.class, getUserId(req), deviceId); getActiveDevice(deviceId).write(command); sendResponse(resp.getWriter(), true); } diff --git a/src/org/traccar/web/JsonConverter.java b/src/org/traccar/web/JsonConverter.java index c01ce8bd6..2ef61fb13 100644 --- a/src/org/traccar/web/JsonConverter.java +++ b/src/org/traccar/web/JsonConverter.java @@ -43,6 +43,14 @@ public final class JsonConverter { private JsonConverter() { } + private static <T> T newClassInstance(Class<T> clazz) { + try { + return clazz.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalArgumentException(); + } + } + private static final DateTimeFormatter DATE_FORMAT = ISODateTimeFormat.dateTime(); public static Date parseDate(String value) { @@ -57,9 +65,23 @@ public final class JsonConverter { public static <T extends Factory> T objectFromJson(JsonObject json, T prototype) { T object = (T) prototype.create(); + Method[] methods = object.getClass().getMethods(); + return objectFromJson(json, object, methods); + } + public static <T> T objectFromJson(Reader reader, Class<T> clazz) throws ParseException { + try (JsonReader jsonReader = Json.createReader(reader)) { + return objectFromJson(jsonReader.readObject(), clazz); + } + } + + public static <T> T objectFromJson(JsonObject json, Class<T> clazz) { + T object = newClassInstance(clazz); Method[] methods = object.getClass().getMethods(); + return objectFromJson(json, object, methods); + } + private static <T> T objectFromJson(JsonObject json, T object, Method[] methods) { for (final Method method : methods) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) { @@ -91,7 +113,6 @@ public final class JsonConverter { } } } - return object; } diff --git a/src/org/traccar/web/MainServlet.java b/src/org/traccar/web/MainServlet.java index 63ff27813..40bfcddb5 100644 --- a/src/org/traccar/web/MainServlet.java +++ b/src/org/traccar/web/MainServlet.java @@ -45,7 +45,7 @@ public class MainServlet extends BaseServlet { } private void session(HttpServletRequest req, HttpServletResponse resp) throws Exception { - Long userId = (Long) req.getSession().getAttribute(USER_KEY); + Long userId = (Long) req.getSession().getAttribute(USER_ID_KEY); if (userId != null) { sendResponse(resp.getWriter(), JsonConverter.objectToJson( Context.getDataManager().getUser(userId))); @@ -58,7 +58,7 @@ public class MainServlet extends BaseServlet { User user = Context.getDataManager().login( req.getParameter("email"), req.getParameter("password")); if (user != null) { - req.getSession().setAttribute(USER_KEY, user.getId()); + req.getSession().setAttribute(USER_ID_KEY, user.getId()); sendResponse(resp.getWriter(), JsonConverter.objectToJson(user)); } else { sendResponse(resp.getWriter(), false); @@ -66,12 +66,12 @@ public class MainServlet extends BaseServlet { } private void logout(HttpServletRequest req, HttpServletResponse resp) throws Exception { - req.getSession().removeAttribute(USER_KEY); + req.getSession().removeAttribute(USER_ID_KEY); sendResponse(resp.getWriter(), true); } private void register(HttpServletRequest req, HttpServletResponse resp) throws Exception { - User user = JsonConverter.objectFromJson(req.getReader(), new User()); + User user = JsonConverter.objectFromJson(req.getReader(), User.class); Context.getDataManager().addUser(user); sendResponse(resp.getWriter(), true); } diff --git a/src/org/traccar/web/WebServer.java b/src/org/traccar/web/WebServer.java index f5a6acdd9..317e4db46 100644 --- a/src/org/traccar/web/WebServer.java +++ b/src/org/traccar/web/WebServer.java @@ -24,7 +24,13 @@ import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.webapp.WebAppContext; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.servlet.ServletContainer; import org.traccar.Config; +import org.traccar.api.CorsResponseFilter; +import org.traccar.api.SecurityRequestFilter; +import org.traccar.api.resource.DeviceResource; +import org.traccar.api.resource.UserResource; import org.traccar.helper.Log; /** @@ -101,6 +107,12 @@ public class WebServer { } private void initApi() { + initOldApi(); + initRestApi(); + } + + @Deprecated + private void initOldApi() { ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); servletHandler.setContextPath("/api"); servletHandler.addServlet(new ServletHolder(new AsyncServlet()), "/async/*"); @@ -120,6 +132,17 @@ public class WebServer { handlers.addHandler(servletHandler); } + private void initRestApi() { + ResourceConfig resourceConfig = new ResourceConfig(); + resourceConfig.register(SecurityRequestFilter.class); + resourceConfig.register(CorsResponseFilter.class); + resourceConfig.registerClasses(DeviceResource.class, UserResource.class); + ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); + ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig)); + servletHandler.addServlet(servletHolder, "/rest/*"); + handlers.addHandler(servletHandler); + } + public void start() { try { server.start(); diff --git a/test/org/traccar/web/JsonConverterTest.java b/test/org/traccar/web/JsonConverterTest.java index 5b2d80ebc..2a0055f54 100644 --- a/test/org/traccar/web/JsonConverterTest.java +++ b/test/org/traccar/web/JsonConverterTest.java @@ -20,7 +20,7 @@ public class JsonConverterTest { "\"string\": \"discworld\", " + "\"date\":\"2015-07-09T19:02:17.000Z\"" + "}"), - new Primitives()); + Primitives.class); assertEquals(true, o.getBoolean()); assertEquals(42, o.getInt()); |