Jelajahi Sumber

Auth for senslog

Lukas Cerny 4 tahun lalu
induk
melakukan
38357bf2e0

+ 6 - 0
doc/openApiDescription.yaml

@@ -19,6 +19,12 @@ paths:
         - Version 1
       operationId: insertionToSenslog
       parameters:
+        - in: header
+          name: X-Auth-Token
+          description: "JWT token to authenticate a content."
+          schema:
+            type: string
+            format: jwt
         - name: Operation
           in: query
           required: true

+ 32 - 106
pom.xml

@@ -1,11 +1,13 @@
-<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/maven-v4_0_0.xsd">
+<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/maven-v4_0_0.xsd"
+>
     <modelVersion>4.0.0</modelVersion>
     <groupId>cz.hsrs.maplog</groupId>
     <artifactId>DBService</artifactId>
     <packaging>jar</packaging>
     <version>1.3.5-SNAPSHOT</version>
     <name>dbservice Maven Webapp</name>
-    <url>http://maven.apache.org</url>
 
     <build>
         <finalName>${project.artifactId}-${project.version}</finalName>
@@ -55,45 +57,6 @@
                 </configuration>
             </plugin>
 
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>buildnumber-maven-plugin</artifactId>
-                <version>1.3</version>
-                <executions>
-                    <execution>
-                        <phase>validate</phase>
-                        <goals>
-                            <goal>create</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <doCheck>false</doCheck>
-                    <doUpdate>false</doUpdate>
-                </configuration>
-            </plugin>
-        
-<!-- HACK for testing -->
-    <plugin>
-        <groupId>org.mortbay.jetty</groupId>
-        <artifactId>maven-jetty-plugin</artifactId>
-        <version>6.1.22</version>
-    </plugin>
-<!-- HACK for testing -->
-
-            <plugin>
-                <groupId>org.apache.tomcat.maven</groupId>
-                <artifactId>tomcat7-maven-plugin</artifactId>
-                <version>2.2</version>
-                <configuration>
-                    <url>http://foodie.lesprojekt.cz:8080/manager/text</url> 
-                    <server>foodietomcat</server>
-                    <path>/MapLog</path>
-                    <!--<path>/MapLogLV</path>-->
-                    <!--<path>/MapLogIT</path>-->
-                    <!--<path>/MapLogOT</path>-->
-                </configuration>
-            </plugin>
 
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -112,29 +75,6 @@
                 </configuration>
             </plugin>
 
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>cobertura-maven-plugin</artifactId>
-                <version>2.4</version>
-                <configuration>
-                    <instrumentation>
-                        <ignores>
-                            <ignore>com.example.boringcode.*</ignore>
-                        </ignores>
-                        <excludes>
-                            <exclude>com/example/dullcode/**/*.class</exclude>
-                            <exclude>com/example/**/*Test.class</exclude>
-                        </excludes>
-                    </instrumentation>
-                </configuration>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>clean</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
         </plugins>
         <pluginManagement>
             <plugins>
@@ -161,17 +101,17 @@
         <dependency>
             <groupId>com.zaxxer</groupId>
             <artifactId>HikariCP</artifactId>
-            <version>4.0.3</version>
+            <version>5.0.0</version>
         </dependency>
         <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi3-postgres</artifactId>
-            <version>3.20.0</version>
+            <version>3.21.0</version>
         </dependency>
         <dependency>
             <groupId>org.jdbi</groupId>
             <artifactId>jdbi3-jodatime2</artifactId>
-            <version>3.20.0</version>
+            <version>3.21.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
@@ -183,14 +123,33 @@
             <artifactId>jcommander</artifactId>
             <version>1.81</version>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.11.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>3.18.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-core</artifactId>
+            <version>2.14.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.logging.log4j</groupId>
+            <artifactId>log4j-api</artifactId>
+            <version>2.14.1</version>
+        </dependency>
 
 
 
 
 
 
-
-        <!-- Tests -->
+                <!-- Tests -->
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
@@ -279,22 +238,22 @@
     <dependency>
         <groupId>org.glassfish.jersey.containers</groupId>
         <artifactId>jersey-container-servlet</artifactId>
-        <version>${jersey2.version}</version>
+        <version>2.23</version>
     </dependency>
     <dependency>
         <groupId>org.glassfish.jersey.core</groupId>
         <artifactId>jersey-client</artifactId>
-        <version>${jersey2.version}</version>
+        <version>2.23</version>
     </dependency>
     <dependency>
         <groupId>org.glassfish.jersey.media</groupId>
         <artifactId>jersey-media-json-jackson</artifactId>
-        <version>${jersey2.version}</version>
+        <version>2.23</version>
     </dependency>
     <dependency>
         <groupId>org.glassfish.jersey.media</groupId>
         <artifactId>jersey-media-multipart</artifactId>
-        <version>${jersey2.version}</version>
+        <version>2.23</version>
     </dependency>
 <!-- JERSEY -->
 
@@ -336,18 +295,9 @@
         </dependency>
 
 
-        <dependency>
-            <groupId>javax.xml.bind</groupId>
-            <artifactId>jaxb-api</artifactId>
-            <version>2.3.1</version>
-        </dependency>
-
-
     </dependencies>
 
     <properties>
-       <jetty.version>6.1.22</jetty.version>
-       <jersey2.version>2.23</jersey2.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.resourceEncoding>UTF-8</project.build.resourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
@@ -355,28 +305,4 @@
        <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
     </properties>
 
-    <reporting>
-        <plugins>
-            <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>cobertura-maven-plugin</artifactId>
-                <version>2.4</version>
-            </plugin>
-        </plugins>
-    </reporting>
-
-    <scm>
-        <connection>scm:git:git+ssh://git@git.ccss.cz/ccss/maplogagri</connection>
-        <developerConnection>scm:git:git+ssh://git@git.ccss.cz/ccss/maplogagri</developerConnection>
-        <url>http://somerepository.com/view.cvs/tags/DBService-1.3</url>
-    </scm>
-
-    <repositories>
-        <repository>
-            <id>com.mvnrepository</id>
-            <name>mvnrepository</name>
-            <url>http://mvnrepository.com</url>
-        </repository>
-    </repositories>
-
 </project>

+ 2 - 3
src/main/java/cz/hsrs/rest/provider/ObservationRest.java

@@ -17,7 +17,6 @@ import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
-import org.mortbay.jetty.HttpStatus;
 
 import cz.hsrs.db.util.ExportUtil;
 import cz.hsrs.db.util.UserUtil;
@@ -76,11 +75,11 @@ public class ObservationRest {
             }
             
         } catch (SQLException e) {
-            return Response.status(HttpStatus.ORDINAL_500_Internal_Server_Error)
+            return Response.status(500)
                     .entity(new ExceptionBean(e.getClass().getName(), e.getLocalizedMessage()))
                     .build();
         } catch (AuthenticationException e) {
-        	return Response.status(HttpStatus.ORDINAL_401_Unauthorized)
+        	return Response.status(401)
                     .entity(new ExceptionBean(e.getClass().getName(), e.getLocalizedMessage()))
                     .build();
 		}

+ 1 - 1
src/main/java/cz/hsrs/rest/provider/UserRest.java

@@ -88,7 +88,7 @@ public class UserRest {
             		return Response.ok()
                             .build();
             	} else {
-                    return Response.status(HttpStatus.ORDINAL_409_Conflict)
+                    return Response.status(409)
                             .entity(new ExceptionBean("Exception", "User with given name cannot be created!"))
                             .build();
             	}

+ 4 - 1
src/main/java/io/senslog/app/JettyServer.java

@@ -1,6 +1,9 @@
 package io.senslog.app;
 
+import io.senslog.ws.model.WSError;
 import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
 import org.eclipse.jetty.webapp.WebAppContext;
 
 public class JettyServer {
@@ -30,4 +33,4 @@ public class JettyServer {
             server.stop();
         }
     }
-}
+}

+ 1 - 1
src/main/java/io/senslog/app/Main.java

@@ -17,7 +17,7 @@ public class Main {
 
         DBConfig dbConfig = new DBConfig(
                 "jdbc:postgresql://localhost:5432/senslog1",
-                "postgres", "root", 6
+                "senslog_app", "SENSlog", 6
         );
         DBRepositoryPool.create(dbConfig);
 

+ 9 - 0
src/main/java/io/senslog/service/AuthService.java

@@ -0,0 +1,9 @@
+package io.senslog.service;
+
+import io.senslog.service.auth.AuthRole;
+
+public interface AuthService {
+
+    boolean isAuthenticate(String token);
+    boolean isAuthorize(String token, AuthRole... roles);
+}

+ 12 - 0
src/main/java/io/senslog/service/auth/Auth.java

@@ -0,0 +1,12 @@
+package io.senslog.service.auth;
+
+import javax.ws.rs.NameBinding;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+@NameBinding
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Auth {
+   AuthRole[] value();
+}

+ 8 - 0
src/main/java/io/senslog/service/auth/AuthRole.java

@@ -0,0 +1,8 @@
+package io.senslog.service.auth;
+
+public enum AuthRole {
+    ADMIN, /* Manage everything */
+    USER,  /* Access data resources  */
+    INFO,  /* Required authentication but not access to data resources */
+    NONE   /* Public endpoints only */
+}

+ 50 - 0
src/main/java/io/senslog/service/auth/OAuthService.java

@@ -0,0 +1,50 @@
+package io.senslog.service.auth;
+
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import io.senslog.service.AuthService;
+
+public class OAuthService implements AuthService {
+
+    private static final OAuthService INSTANCE;
+
+//    private final JWTVerifier verifier;
+
+    static {
+        INSTANCE = new OAuthService();
+    }
+
+    public static OAuthService getInstance() {
+        return INSTANCE;
+    }
+
+    private OAuthService() {
+//        this.verifier = JWT.require(Algorithm.HMAC256("secret"))
+//                .withIssuer("auth0")
+//                .build();
+
+    }
+
+    @Override
+    public boolean isAuthenticate(String token) {
+        if (token == null || token.isEmpty()) {
+            return true;
+        }
+
+        try {
+     //       DecodedJWT jwt = verifier.verify(token);
+            // TODO validate
+
+            return true;
+        } catch (JWTVerificationException e){
+            //Invalid signature/claims
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isAuthorize(String token, AuthRole... roles) {
+        // TODO authorize token via properties
+        return true;
+    }
+}

+ 0 - 30
src/main/java/io/senslog/ws/filter/AuthFilter.java

@@ -1,30 +0,0 @@
-package io.senslog.ws.filter;
-
-import io.senslog.ws.model.WSError;
-
-import javax.ws.rs.container.ContainerRequestContext;
-import javax.ws.rs.container.ContainerRequestFilter;
-import javax.ws.rs.container.PreMatching;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
-import java.io.IOException;
-
-import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
-
-// @PreMatching @Provider
-public class AuthFilter implements ContainerRequestFilter {
-
-    @Override
-    public void filter(ContainerRequestContext requestContext) throws IOException {
-
-        String xAuth = requestContext.getHeaderString("x-auth");
-
-        if (xAuth == null || xAuth.isEmpty()) {
-            requestContext.abortWith(Response
-                    .status(Response.Status.UNAUTHORIZED)
-                    .type(APPLICATION_JSON)
-                    .entity(new WSError("Unauthorized. You have to login at first.").toJson())
-                    .build());
-        }
-    }
-}

+ 61 - 0
src/main/java/io/senslog/ws/filter/AuthorizationRequestFilter.java

@@ -0,0 +1,61 @@
+package io.senslog.ws.filter;
+
+import io.senslog.service.AuthService;
+import io.senslog.service.auth.*;
+import io.senslog.ws.model.WSError;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+import java.io.IOException;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+@Provider
+@Auth(AuthRole.NONE)
+@Priority(Priorities.AUTHORIZATION)
+public class AuthorizationRequestFilter implements ContainerRequestFilter {
+
+    private final AuthService authService;
+
+    @Context
+    private ResourceInfo resourceInfo;
+
+    public AuthorizationRequestFilter() {
+        this.authService = OAuthService.getInstance();
+    }
+
+    @Override
+    public void filter(ContainerRequestContext ctx) throws IOException {
+        String token = ctx.getHeaderString("X-Auth-Token");
+        if (!authService.isAuthenticate(token)) {
+            ctx.abortWith(Response
+                    .status(Response.Status.UNAUTHORIZED)
+                    .type(APPLICATION_JSON)
+                    .entity(new WSError("Unauthorized. You have to login at first.").toJson())
+                    .build());
+        }
+
+        Auth authAnnotation = resourceInfo.getResourceMethod().getAnnotation(Auth.class);
+        if (authAnnotation == null) {
+            String endpointName = ctx.getUriInfo().getPath();
+            System.err.println("Accessing resource '"+endpointName+"' does not specify any authorization rules.");
+            ctx.abortWith(Response
+                    .status(Response.Status.UNAUTHORIZED)
+                    .type(APPLICATION_JSON)
+                    .entity(new WSError("Accessing resource '"+endpointName+"' does not specify any authorization rules.").toJson())
+                    .build());
+        }else if (!authService.isAuthorize(token, authAnnotation.value())) {
+            ctx.abortWith(Response
+                    .status(Response.Status.FORBIDDEN)
+                    .type(APPLICATION_JSON)
+                    .entity(new WSError("Access denied.").toJson())
+                    .build());
+        }
+    }
+}

+ 7 - 3
src/main/java/io/senslog/ws/handler/InfoHandler.java

@@ -1,27 +1,31 @@
 package io.senslog.ws.handler;
 
 import io.senslog.core.AppInfo;
+import io.senslog.service.auth.Auth;
+import io.senslog.service.auth.AuthRole;
 import net.sf.json.JSONObject;
 
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Response;
-import java.util.concurrent.TimeUnit;
 
 import static io.senslog.core.AppInfo.appVersion;
 import static io.senslog.core.AppInfo.buildVersion;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 
 @Path("/info")
 public class InfoHandler {
 
+    @Auth(AuthRole.ADMIN)
     @GET @Produces(APPLICATION_JSON)
     public Response getOverallInfo() {
         long uptimeMillis = AppInfo.uptime();
         String uptime = String.format("%02d min %02d sec",
-                TimeUnit.MILLISECONDS.toMinutes(uptimeMillis),
-                TimeUnit.MILLISECONDS.toSeconds(uptimeMillis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(uptimeMillis))
+                MILLISECONDS.toMinutes(uptimeMillis),
+                MILLISECONDS.toSeconds(uptimeMillis) - MINUTES.toSeconds(MILLISECONDS.toMinutes(uptimeMillis))
         );
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("uptime", uptime);

+ 4 - 0
src/main/java/io/senslog/ws/handler/v1/DataPublishingHandler.java

@@ -1,6 +1,8 @@
 package io.senslog.ws.handler.v1;
 
 import io.senslog.service.DataService;
+import io.senslog.service.auth.Auth;
+import io.senslog.service.auth.AuthRole;
 import io.senslog.util.NumberUtils;
 import io.senslog.ws.WSException;
 import io.senslog.ws.model.WSObservation;
@@ -35,6 +37,7 @@ public class DataPublishingHandler {
     }
 
     public DataPublishingHandler() {
+        // TODO refactor with injection
         this.dataService = DataService.newInstance();
 
         operationFnc = new HashMap<>();
@@ -43,6 +46,7 @@ public class DataPublishingHandler {
         operationFnc.put("GetObservations",     this::loadAllObservations);
     }
 
+    @Auth(AuthRole.USER)
     @Path("/SensorService")
     @GET @Produces(APPLICATION_JSON)
     public Response handleGetOperation(@QueryParam("Operation") @DefaultValue("") String operationType, @Context UriInfo allUri) {

+ 9 - 4
src/main/java/io/senslog/ws/handler/v1/DataRetrievingHandler.java

@@ -1,6 +1,8 @@
 package io.senslog.ws.handler.v1;
 
 import io.senslog.service.DataService;
+import io.senslog.service.auth.Auth;
+import io.senslog.service.auth.AuthRole;
 import io.senslog.ws.WSException;
 import io.senslog.ws.model.WSObservation;
 import io.senslog.ws.model.WSPosition;
@@ -32,6 +34,7 @@ public class DataRetrievingHandler {
     }
 
     public DataRetrievingHandler() {
+        // TODO refactor with injection
         this.dataService = DataService.newInstance();
 
         operationFnc = new HashMap<>();
@@ -41,16 +44,18 @@ public class DataRetrievingHandler {
         operationFnc.put("SolvingAlertEvent", this::solvingAlertEvent);
     }
 
+    @Auth(AuthRole.USER)
     @Path("/FeederServlet")
     @POST @Produces(TEXT_PLAIN)
-    public Response handlePostOperation(@QueryParam("Operation") @DefaultValue("") String operationType, @Context UriInfo allUri) {
-        return operationFnc.getOrDefault(operationType, DEFAULT_FNC).apply(allUri.getQueryParameters());
+    public Response handlePostOperation(@QueryParam("Operation") @DefaultValue("") String opType, @Context UriInfo info) {
+        return operationFnc.getOrDefault(opType, DEFAULT_FNC).apply(info.getQueryParameters());
     }
 
+    @Auth(AuthRole.USER)
     @Path("/FeederServlet")
     @GET @Produces(TEXT_PLAIN)
-    public Response handleGetOperation(@QueryParam("Operation") @DefaultValue("") String operationType, @Context UriInfo allUri) {
-        return operationFnc.getOrDefault(operationType, DEFAULT_FNC).apply(allUri.getQueryParameters());
+    public Response handleGetOperation(@QueryParam("Operation") @DefaultValue("") String opType, @Context UriInfo info) {
+        return operationFnc.getOrDefault(opType, DEFAULT_FNC).apply(info.getQueryParameters());
     }
 
     private Response saveNewObservation(MultivaluedMap<String, String> params) {