From 26ba20d9644774688b15fc39fd9507d90e8fa042 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Fri, 8 Dec 2023 20:14:47 +0100 Subject: [PATCH] [DEV] continue integrations --- pom.xml | 2 +- .../kar/archidata/dataAccess/DataAccess.java | 11 ++- .../filter/AuthenticationFilter.java | 28 +++--- .../archidata/migration/model/Migration1.java | 35 +++++++ src/org/kar/archidata/tools/JWTWrapper.java | 18 ++-- src/org/kar/archidata/tools/RESTApi.java | 18 +++- test/src/test/kar/archidata/TestListJson.java | 99 +++++++++++++++++++ test/src/test/kar/archidata/TestTypes.java | 2 +- .../archidata/model/SerializeListAsJson.java | 13 +++ 9 files changed, 194 insertions(+), 32 deletions(-) create mode 100644 src/org/kar/archidata/migration/model/Migration1.java create mode 100644 test/src/test/kar/archidata/TestListJson.java create mode 100644 test/src/test/kar/archidata/model/SerializeListAsJson.java diff --git a/pom.xml b/pom.xml index 42c091f..f6408b0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 kangaroo-and-rabbit archidata - 0.5.0 + 0.5.1 3.1 21 diff --git a/src/org/kar/archidata/dataAccess/DataAccess.java b/src/org/kar/archidata/dataAccess/DataAccess.java index 8e6d6e6..cd80caa 100644 --- a/src/org/kar/archidata/dataAccess/DataAccess.java +++ b/src/org/kar/archidata/dataAccess/DataAccess.java @@ -480,7 +480,7 @@ public class DataAccess { if (options != null) { final CheckFunction check = options.get(CheckFunction.class); if (check != null) { - check.getChecker().check(data, null); + check.getChecker().check(data, AnnotationTools.getFieldsNames(clazz)); } } @@ -655,11 +655,11 @@ public class DataAccess { * @param jsonData Json data (partial) values to update * @return the number of object updated * @throws Exception */ - public static int updateWithJson(final Class clazz, final ID_TYPE id, final String jsonData) throws Exception { - return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData); + public static int updateWithJson(final Class clazz, final ID_TYPE id, final String jsonData, final QueryOptions options) throws Exception { + return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData, options); } - public static int updateWhereWithJson(final Class clazz, final QueryItem condition, final String jsonData) throws Exception { + public static int updateWhereWithJson(final Class clazz, final QueryItem condition, final String jsonData, final QueryOptions options) throws Exception { final ObjectMapper mapper = new ObjectMapper(); // parse the object to be sure the data are valid: final T data = mapper.readValue(jsonData, clazz); @@ -668,7 +668,8 @@ public class DataAccess { final List keys = new ArrayList<>(); final var iterator = root.fieldNames(); iterator.forEachRemaining(e -> keys.add(e)); - return updateWhere(data, condition, null, keys); + // TODO: set the filter in the Options... + return updateWhere(data, condition, options, keys); } public static int update(final T data, final ID_TYPE id) throws Exception { diff --git a/src/org/kar/archidata/filter/AuthenticationFilter.java b/src/org/kar/archidata/filter/AuthenticationFilter.java index c2b97c0..7582de5 100644 --- a/src/org/kar/archidata/filter/AuthenticationFilter.java +++ b/src/org/kar/archidata/filter/AuthenticationFilter.java @@ -38,7 +38,7 @@ import jakarta.ws.rs.ext.Provider; @Provider @Priority(Priorities.AUTHENTICATION) public class AuthenticationFilter implements ContainerRequestFilter { - final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class); + private final static Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class); @Context private ResourceInfo resourceInfo; protected final String applicationName; @@ -57,7 +57,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { final Method method = this.resourceInfo.getResourceMethod(); // Access denied for all if (method.isAnnotationPresent(DenyAll.class)) { - this.logger.debug(" ==> deny all {}", requestContext.getUriInfo().getPath()); + LOGGER.debug(" ==> deny all {}", requestContext.getUriInfo().getPath()); requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build()); return; } @@ -70,7 +70,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { } // this is a security guard, all the API must define their access level: if (!method.isAnnotationPresent(RolesAllowed.class)) { - this.logger.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); + LOGGER.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build()); return; } @@ -94,7 +94,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader); // Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)" if (!isApplicationToken && !isJwtToken) { - this.logger.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath()); + LOGGER.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath()); abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath()); return; } @@ -106,12 +106,12 @@ public class AuthenticationFilter implements ContainerRequestFilter { try { userByToken = validateJwtToken(token); } catch (final Exception e) { - this.logger.error("Fail to validate token: {}", e.getMessage()); + LOGGER.error("Fail to validate token: {}", e.getMessage()); abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage()); return; } if (userByToken == null) { - this.logger.warn("get a NULL user ..."); + LOGGER.warn("get a NULL user ..."); abortWithUnauthorized(requestContext, "get a NULL user ..."); return; } @@ -122,12 +122,12 @@ public class AuthenticationFilter implements ContainerRequestFilter { try { userByToken = validateToken(token); } catch (final Exception e) { - this.logger.error("Fail to validate token: {}", e.getMessage()); + LOGGER.error("Fail to validate token: {}", e.getMessage()); abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage()); return; } if (userByToken == null) { - this.logger.warn("get a NULL application ..."); + LOGGER.warn("get a NULL application ..."); abortWithUnauthorized(requestContext, "get a NULL application ..."); return; } @@ -149,7 +149,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { } // Is user valid? if (!haveRight) { - this.logger.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles); + LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles); requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build()); return; } @@ -175,15 +175,15 @@ public class AuthenticationFilter implements ContainerRequestFilter { // Abort the filter chain with a 401 status code response // The WWW-Authenticate header is sent along with the response - this.logger.warn("abortWithUnauthorized:"); + LOGGER.warn("abortWithUnauthorized:"); final RestErrorResponse ret = new RestErrorResponse(Response.Status.UNAUTHORIZED, "Unauthorized", message); - this.logger.error("Error UUID={}", ret.uuid); + LOGGER.error("Error UUID={}", ret.uuid); requestContext.abortWith(Response.status(ret.status).header(HttpHeaders.WWW_AUTHENTICATE, AUTHENTICATION_SCHEME + " base64(HEADER).base64(CONTENT).base64(KEY)").entity(ret) .type(MediaType.APPLICATION_JSON).build()); } protected UserByToken validateToken(final String authorization) throws Exception { - this.logger.info("Must be Override by the application implmentation, otherwise it dose not work"); + LOGGER.info("Must be Override by the application implmentation, otherwise it dose not work"); return null; } @@ -193,7 +193,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); // check the token is valid !!! (signed and coherent issuer... if (ret == null) { - this.logger.error("The token is not valid: '{}'", authorization); + LOGGER.error("The token is not valid: '{}'", authorization); return null; } // check userID @@ -209,7 +209,7 @@ public class AuthenticationFilter implements ContainerRequestFilter { if (rights.containsKey(this.applicationName)) { user.right = rights.get(this.applicationName); } else { - this.logger.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights); + LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights); } } // logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight); diff --git a/src/org/kar/archidata/migration/model/Migration1.java b/src/org/kar/archidata/migration/model/Migration1.java new file mode 100644 index 0000000..17d5092 --- /dev/null +++ b/src/org/kar/archidata/migration/model/Migration1.java @@ -0,0 +1,35 @@ +package org.kar.archidata.migration.model; + +import org.kar.archidata.annotation.DataComment; +import org.kar.archidata.annotation.DataDefault; +import org.kar.archidata.annotation.DataIfNotExists; +import org.kar.archidata.model.GenericDataSoftDelete; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import jakarta.persistence.Column; +import jakarta.persistence.Table; + +// For logs only +//public static final String TABLE_NAME = "KAR_migration"; + +@Table(name = "KAR_migration") +@DataIfNotExists +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Migration1 extends GenericDataSoftDelete { + final static int VERSION_MIGRATION = 1; + @DataComment("Name of the migration") + @Column(length = 256) + public String name; + @Column(nullable = false) + @DataDefault("'0'") + @DataComment("if the migration is well terminated or not") + public Boolean terminated = false; + @DataComment("index in the migration progression") + public Integer stepId = 0; + @DataComment("number of element in the migration") + public Integer count; + @DataComment("Log generate by the migration") + @Column(length = 0) + public String log = ""; +} diff --git a/src/org/kar/archidata/tools/JWTWrapper.java b/src/org/kar/archidata/tools/JWTWrapper.java index 4ae652a..d38725a 100644 --- a/src/org/kar/archidata/tools/JWTWrapper.java +++ b/src/org/kar/archidata/tools/JWTWrapper.java @@ -51,13 +51,13 @@ class TestSigner implements JWSSigner { @Override public Set supportedJWSAlgorithms() { // TODO Auto-generated method stub - return null; + return Set.of(JWSAlgorithm.RS256); } @Override public JCAContext getJCAContext() { // TODO Auto-generated method stub - return null; + return new JCAContext(); } } @@ -209,8 +209,9 @@ public class JWTWrapper { try { // On the consumer side, parse the JWS and verify its RSA signature final SignedJWT signedJWT = SignedJWT.parse(signedToken); - - if (rsaPublicJWK == null) { + if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) { + LOGGER.warn("Someone use a test token: {}", signedToken); + } else if (rsaPublicJWK == null) { LOGGER.warn("JWT public key is not present !!!"); if (!ConfigBaseVariable.getTestMode()) { return null; @@ -251,16 +252,16 @@ public class JWTWrapper { return null; } - public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map rights) { + public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map> rights) { if (!ConfigBaseVariable.getTestMode()) { LOGGER.error("Test mode disable !!!!!"); return null; } try { - final int timeOutInMunites = 3600 * 24 * 31; + final int timeOutInMunites = 3600; final Date now = new Date(); - final Date expiration = new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */); + final Date expiration = new Date(new Date().getTime() + timeOutInMunites * 1000 /* ms */); final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().subject(Long.toString(userID)).claim("login", userLogin).claim("application", application).issuer(isuer).issueTime(now) .expirationTime(expiration); // Do not ask why we need a "-" here ... this have no meaning @@ -278,7 +279,8 @@ public class JWTWrapper { // serialize the output... return signedJWT.serialize(); } catch (final Exception ex) { - LOGGER.error("Can not generate Test Token..."); + ex.printStackTrace(); + LOGGER.error("Can not generate Test Token... {}", ex.getLocalizedMessage()); } return null; } diff --git a/src/org/kar/archidata/tools/RESTApi.java b/src/org/kar/archidata/tools/RESTApi.java index e0571e3..a987701 100644 --- a/src/org/kar/archidata/tools/RESTApi.java +++ b/src/org/kar/archidata/tools/RESTApi.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; import jakarta.ws.rs.core.HttpHeaders; @@ -60,8 +61,12 @@ public class RESTApi { final HttpResponse httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { // LOGGER.error("catch error from REST API: {}", httpResponse.body()); - final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); - throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); + try { + final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); + throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); + } catch (final MismatchedInputException ex) { + throw new IOException("Fail to get the data [" + httpResponse.statusCode() + "] " + httpResponse.body()); + } } // LOGGER.error("status code: {}", httpResponse.statusCode()); // LOGGER.error("data: {}", httpResponse.body()); @@ -120,9 +125,16 @@ public class RESTApi { public T put(final Class clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { final ObjectMapper mapper = new ObjectMapper(); - final HttpClient client = HttpClient.newHttpClient(); final String body = mapper.writeValueAsString(data); + return putJson(clazz, urlOffset, body); + } + + public T putJson(final Class clazz, final String urlOffset, final String body) throws RESTErrorResponseExeption, IOException, InterruptedException { + final ObjectMapper mapper = new ObjectMapper(); + final HttpClient client = HttpClient.newHttpClient(); Builder requestBuilding = HttpRequest.newBuilder().uri(URI.create(this.baseUrl + urlOffset)); + LOGGER.trace("call PUT: {}", URI.create(this.baseUrl + urlOffset)); + LOGGER.trace("DATA: {}", body); if (this.token != null) { requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); } diff --git a/test/src/test/kar/archidata/TestListJson.java b/test/src/test/kar/archidata/TestListJson.java new file mode 100644 index 0000000..9875698 --- /dev/null +++ b/test/src/test/kar/archidata/TestListJson.java @@ -0,0 +1,99 @@ +package test.kar.archidata; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.kar.archidata.GlobalConfiguration; +import org.kar.archidata.dataAccess.DataAccess; +import org.kar.archidata.dataAccess.DataFactory; +import org.kar.archidata.db.DBEntry; +import org.kar.archidata.tools.ConfigBaseVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import test.kar.archidata.model.SerializeListAsJson; + +@ExtendWith(StepwiseExtension.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TestListJson { + final static private Logger LOGGER = LoggerFactory.getLogger(TestListJson.class); + + @BeforeAll + public static void configureWebServer() throws Exception { + if (!"true".equalsIgnoreCase(System.getenv("TEST_E2E_MODE"))) { + ConfigBaseVariable.dbType = "sqlite"; + ConfigBaseVariable.dbHost = "memory"; + // for test we need to connect all time the DB + ConfigBaseVariable.dbKeepConnected = "true"; + } + // Connect the dataBase... + final DBEntry entry = DBEntry.createInterface(GlobalConfiguration.dbConfig); + entry.connect(); + } + + @AfterAll + public static void removeDataBase() throws IOException { + LOGGER.info("Remove the test db"); + DBEntry.closeAllForceMode(); + ConfigBaseVariable.clearAllValue(); + } + + @Order(1) + @Test + public void testTableInsertAndRetrieve() throws Exception { + final List sqlCommand = DataFactory.createTable(SerializeListAsJson.class); + for (final String elem : sqlCommand) { + LOGGER.debug("request: '{}'", elem); + DataAccess.executeSimpleQuerry(elem, false); + } + } + + @Order(2) + @Test + public void testIO() throws Exception { + final SerializeListAsJson test = new SerializeListAsJson(); + test.data = new ArrayList<>(); + test.data.add(5); + test.data.add(2); + test.data.add(8); + test.data.add(6); + test.data.add(51); + + final SerializeListAsJson insertedData = DataAccess.insert(test); + + Assertions.assertNotNull(insertedData); + Assertions.assertNotNull(insertedData.id); + Assertions.assertTrue(insertedData.id >= 0); + Assertions.assertNotNull(insertedData.data); + Assertions.assertEquals(5, insertedData.data.size()); + Assertions.assertEquals(test.data.get(0), insertedData.data.get(0)); + Assertions.assertEquals(test.data.get(1), insertedData.data.get(1)); + Assertions.assertEquals(test.data.get(2), insertedData.data.get(2)); + Assertions.assertEquals(test.data.get(3), insertedData.data.get(3)); + Assertions.assertEquals(test.data.get(4), insertedData.data.get(4)); + + // Try to retrieve all the data: + final SerializeListAsJson retrieve = DataAccess.get(SerializeListAsJson.class, insertedData.id); + + Assertions.assertNotNull(retrieve); + Assertions.assertNotNull(retrieve.id); + Assertions.assertTrue(retrieve.id >= 0); + Assertions.assertNotNull(retrieve.data); + Assertions.assertEquals(5, retrieve.data.size()); + Assertions.assertEquals(test.data.get(0), retrieve.data.get(0)); + Assertions.assertEquals(test.data.get(1), retrieve.data.get(1)); + Assertions.assertEquals(test.data.get(2), retrieve.data.get(2)); + Assertions.assertEquals(test.data.get(3), retrieve.data.get(3)); + Assertions.assertEquals(test.data.get(4), retrieve.data.get(4)); + } + +} diff --git a/test/src/test/kar/archidata/TestTypes.java b/test/src/test/kar/archidata/TestTypes.java index 89a0903..0c87596 100644 --- a/test/src/test/kar/archidata/TestTypes.java +++ b/test/src/test/kar/archidata/TestTypes.java @@ -449,7 +449,7 @@ public class TestTypes { "varcharData": null } """; - final int nbUpdate = DataAccess.updateWithJson(TypesTable.class, insertedData.id, jsonData); + final int nbUpdate = DataAccess.updateWithJson(TypesTable.class, insertedData.id, jsonData, null); Assertions.assertEquals(1, nbUpdate); // Get new data diff --git a/test/src/test/kar/archidata/model/SerializeListAsJson.java b/test/src/test/kar/archidata/model/SerializeListAsJson.java new file mode 100644 index 0000000..7fbda65 --- /dev/null +++ b/test/src/test/kar/archidata/model/SerializeListAsJson.java @@ -0,0 +1,13 @@ +package test.kar.archidata.model; + +import java.util.List; + +import org.kar.archidata.annotation.DataJson; +import org.kar.archidata.model.GenericData; + +public class SerializeListAsJson extends GenericData { + + @DataJson + public List data; + +} \ No newline at end of file