[DEV] continue integrations

This commit is contained in:
Edouard DUPIN 2023-12-08 20:14:47 +01:00
parent 659f9ca306
commit 26ba20d964
9 changed files with 194 additions and 32 deletions

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.5.0</version> <version>0.5.1</version>
<properties> <properties>
<maven.compiler.version>3.1</maven.compiler.version> <maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>

View File

@ -480,7 +480,7 @@ public class DataAccess {
if (options != null) { if (options != null) {
final CheckFunction check = options.get(CheckFunction.class); final CheckFunction check = options.get(CheckFunction.class);
if (check != null) { 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 * @param jsonData Json data (partial) values to update
* @return the number of object updated * @return the number of object updated
* @throws Exception */ * @throws Exception */
public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData) throws Exception { public static <T, ID_TYPE> int updateWithJson(final Class<T> clazz, final ID_TYPE id, final String jsonData, final QueryOptions options) throws Exception {
return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData); return updateWhereWithJson(clazz, getTableIdCondition(clazz, id), jsonData, options);
} }
public static <T> int updateWhereWithJson(final Class<T> clazz, final QueryItem condition, final String jsonData) throws Exception { public static <T> int updateWhereWithJson(final Class<T> clazz, final QueryItem condition, final String jsonData, final QueryOptions options) throws Exception {
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
// parse the object to be sure the data are valid: // parse the object to be sure the data are valid:
final T data = mapper.readValue(jsonData, clazz); final T data = mapper.readValue(jsonData, clazz);
@ -668,7 +668,8 @@ public class DataAccess {
final List<String> keys = new ArrayList<>(); final List<String> keys = new ArrayList<>();
final var iterator = root.fieldNames(); final var iterator = root.fieldNames();
iterator.forEachRemaining(e -> keys.add(e)); 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 <T, ID_TYPE> int update(final T data, final ID_TYPE id) throws Exception { public static <T, ID_TYPE> int update(final T data, final ID_TYPE id) throws Exception {

View File

@ -38,7 +38,7 @@ import jakarta.ws.rs.ext.Provider;
@Provider @Provider
@Priority(Priorities.AUTHENTICATION) @Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter { public class AuthenticationFilter implements ContainerRequestFilter {
final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class); private final static Logger LOGGER = LoggerFactory.getLogger(AuthenticationFilter.class);
@Context @Context
private ResourceInfo resourceInfo; private ResourceInfo resourceInfo;
protected final String applicationName; protected final String applicationName;
@ -57,7 +57,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final Method method = this.resourceInfo.getResourceMethod(); final Method method = this.resourceInfo.getResourceMethod();
// Access denied for all // Access denied for all
if (method.isAnnotationPresent(DenyAll.class)) { 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()); requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access blocked !!!").build());
return; return;
} }
@ -70,7 +70,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
// this is a security guard, all the API must define their access level: // this is a security guard, all the API must define their access level:
if (!method.isAnnotationPresent(RolesAllowed.class)) { 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()); requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build());
return; return;
} }
@ -94,7 +94,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader); final boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader);
// Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)" // Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)"
if (!isApplicationToken && !isJwtToken) { 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()); abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
return; return;
} }
@ -106,12 +106,12 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try { try {
userByToken = validateJwtToken(token); userByToken = validateJwtToken(token);
} catch (final Exception e) { } 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()); abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage());
return; return;
} }
if (userByToken == null) { if (userByToken == null) {
this.logger.warn("get a NULL user ..."); LOGGER.warn("get a NULL user ...");
abortWithUnauthorized(requestContext, "get a NULL user ..."); abortWithUnauthorized(requestContext, "get a NULL user ...");
return; return;
} }
@ -122,12 +122,12 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try { try {
userByToken = validateToken(token); userByToken = validateToken(token);
} catch (final Exception e) { } 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()); abortWithUnauthorized(requestContext, "Fail to validate token: " + e.getMessage());
return; return;
} }
if (userByToken == null) { if (userByToken == null) {
this.logger.warn("get a NULL application ..."); LOGGER.warn("get a NULL application ...");
abortWithUnauthorized(requestContext, "get a NULL application ..."); abortWithUnauthorized(requestContext, "get a NULL application ...");
return; return;
} }
@ -149,7 +149,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
// Is user valid? // Is user valid?
if (!haveRight) { 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()); requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Not enought RIGHT !!!").build());
return; return;
} }
@ -175,15 +175,15 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// Abort the filter chain with a 401 status code response // Abort the filter chain with a 401 status code response
// The WWW-Authenticate header is sent along with the 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); 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) 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()); .type(MediaType.APPLICATION_JSON).build());
} }
protected UserByToken validateToken(final String authorization) throws Exception { 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; return null;
} }
@ -193,7 +193,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null);
// check the token is valid !!! (signed and coherent issuer... // check the token is valid !!! (signed and coherent issuer...
if (ret == null) { if (ret == null) {
this.logger.error("The token is not valid: '{}'", authorization); LOGGER.error("The token is not valid: '{}'", authorization);
return null; return null;
} }
// check userID // check userID
@ -209,7 +209,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
if (rights.containsKey(this.applicationName)) { if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName); user.right = rights.get(this.applicationName);
} else { } 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); // logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight);

View File

@ -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 = "";
}

View File

@ -51,13 +51,13 @@ class TestSigner implements JWSSigner {
@Override @Override
public Set<JWSAlgorithm> supportedJWSAlgorithms() { public Set<JWSAlgorithm> supportedJWSAlgorithms() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return Set.of(JWSAlgorithm.RS256);
} }
@Override @Override
public JCAContext getJCAContext() { public JCAContext getJCAContext() {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return new JCAContext();
} }
} }
@ -209,8 +209,9 @@ public class JWTWrapper {
try { try {
// On the consumer side, parse the JWS and verify its RSA signature // On the consumer side, parse the JWS and verify its RSA signature
final SignedJWT signedJWT = SignedJWT.parse(signedToken); final SignedJWT signedJWT = SignedJWT.parse(signedToken);
if (ConfigBaseVariable.getTestMode() && signedToken.endsWith(TestSigner.test_signature)) {
if (rsaPublicJWK == null) { LOGGER.warn("Someone use a test token: {}", signedToken);
} else if (rsaPublicJWK == null) {
LOGGER.warn("JWT public key is not present !!!"); LOGGER.warn("JWT public key is not present !!!");
if (!ConfigBaseVariable.getTestMode()) { if (!ConfigBaseVariable.getTestMode()) {
return null; return null;
@ -251,16 +252,16 @@ public class JWTWrapper {
return null; return null;
} }
public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map<String, Object> rights) { public static String createJwtTestToken(final long userID, final String userLogin, final String isuer, final String application, final Map<String, Map<String, Object>> rights) {
if (!ConfigBaseVariable.getTestMode()) { if (!ConfigBaseVariable.getTestMode()) {
LOGGER.error("Test mode disable !!!!!"); LOGGER.error("Test mode disable !!!!!");
return null; return null;
} }
try { try {
final int timeOutInMunites = 3600 * 24 * 31; final int timeOutInMunites = 3600;
final Date now = new Date(); 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) 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 .expirationTime(expiration); // Do not ask why we need a "-" here ... this have no meaning
@ -278,7 +279,8 @@ public class JWTWrapper {
// serialize the output... // serialize the output...
return signedJWT.serialize(); return signedJWT.serialize();
} catch (final Exception ex) { } catch (final Exception ex) {
LOGGER.error("Can not generate Test Token..."); ex.printStackTrace();
LOGGER.error("Can not generate Test Token... {}", ex.getLocalizedMessage());
} }
return null; return null;
} }

View File

@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.HttpHeaders;
@ -60,8 +61,12 @@ public class RESTApi {
final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); final HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) { if (httpResponse.statusCode() < 200 || httpResponse.statusCode() >= 300) {
// LOGGER.error("catch error from REST API: {}", httpResponse.body()); // LOGGER.error("catch error from REST API: {}", httpResponse.body());
try {
final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class); final RESTErrorResponseExeption out = mapper.readValue(httpResponse.body(), RESTErrorResponseExeption.class);
throw new RESTErrorResponseExeption(out.uuid, out.time, out.error, out.message, out.status, out.statusMessage); 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("status code: {}", httpResponse.statusCode());
// LOGGER.error("data: {}", httpResponse.body()); // LOGGER.error("data: {}", httpResponse.body());
@ -120,9 +125,16 @@ public class RESTApi {
public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException { public <T, U> T put(final Class<T> clazz, final String urlOffset, final U data) throws RESTErrorResponseExeption, IOException, InterruptedException {
final ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
final HttpClient client = HttpClient.newHttpClient();
final String body = mapper.writeValueAsString(data); final String body = mapper.writeValueAsString(data);
return putJson(clazz, urlOffset, body);
}
public <T, U> T putJson(final Class<T> 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)); 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) { if (this.token != null) {
requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token); requestBuilding = requestBuilding.header(HttpHeaders.AUTHORIZATION, "Yota " + this.token);
} }

View File

@ -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<String> 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));
}
}

View File

@ -449,7 +449,7 @@ public class TestTypes {
"varcharData": null "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); Assertions.assertEquals(1, nbUpdate);
// Get new data // Get new data

View File

@ -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<Integer> data;
}