[DEV] remove generic annotation and test json update

This commit is contained in:
Edouard DUPIN 2023-11-11 10:47:03 +01:00
parent f69bc8097a
commit c4fc49d91b
12 changed files with 498 additions and 350 deletions

View File

@ -1,14 +0,0 @@
package org.kar.archidata.annotation.security;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.ws.rs.NameBinding;
@NameBinding
@Retention(RUNTIME)
@Target({ METHOD })
public @interface DenyAll {}

View File

@ -1,14 +0,0 @@
package org.kar.archidata.annotation.security;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.ws.rs.NameBinding;
@NameBinding
@Retention(RUNTIME)
@Target({ METHOD })
public @interface PermitAll {}

View File

@ -1,16 +0,0 @@
package org.kar.archidata.annotation.security;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import jakarta.ws.rs.NameBinding;
@NameBinding
@Retention(RUNTIME)
@Target({ METHOD })
public @interface RolesAllowed {
String[] value();
}

View File

@ -22,15 +22,15 @@ import javax.imageio.ImageIO;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam; import org.glassfish.jersey.media.multipart.FormDataParam;
import org.kar.archidata.annotation.security.PermitTokenInURI; import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.annotation.security.RolesAllowed;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.dataAccess.DataAccess; import org.kar.archidata.dataAccess.DataAccess;
import org.kar.archidata.dataAccess.QueryCondition;
import org.kar.archidata.filter.GenericContext; import org.kar.archidata.filter.GenericContext;
import org.kar.archidata.model.Data; import org.kar.archidata.model.Data;
import org.kar.archidata.util.ConfigBaseVariable; import org.kar.archidata.util.ConfigBaseVariable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Consumes; import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.HeaderParam;
@ -62,7 +62,7 @@ public class DataResource {
*/ */
private static long tmpFolderId = 1; private static long tmpFolderId = 1;
private static void createFolder(String path) throws IOException { private static void createFolder(final String path) throws IOException {
if (!Files.exists(java.nio.file.Path.of(path))) { if (!Files.exists(java.nio.file.Path.of(path))) {
//Log.print("Create folder: " + path); //Log.print("Create folder: " + path);
Files.createDirectories(java.nio.file.Path.of(path)); Files.createDirectories(java.nio.file.Path.of(path));
@ -73,89 +73,75 @@ public class DataResource {
return tmpFolderId++; return tmpFolderId++;
} }
public static String getTmpFileInData(long tmpFolderId) { public static String getTmpFileInData(final long tmpFolderId) {
String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId; final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId;
try { try {
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator); createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
} catch (IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return filePath; return filePath;
} }
public static String getFileData(long tmpFolderId) { public static String getFileData(final long tmpFolderId) {
String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data"; final String filePath = ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator + "data";
try { try {
createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator); createFolder(ConfigBaseVariable.getMediaDataFolder() + File.separator + tmpFolderId + File.separator);
} catch (IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return filePath; return filePath;
} }
public static Data getWithSha512(String sha512) { public static Data getWithSha512(final String sha512) {
LOGGER.info("find sha512 = {}", sha512); LOGGER.info("find sha512 = {}", sha512);
try { try {
return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512)); return DataAccess.getWhere(Data.class, new QueryCondition("sha512", "=", sha512));
} catch (Exception e) { } catch (final Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
} }
public static Data getWithId(long id) { public static Data getWithId(final long id) {
LOGGER.info("find id = {}", id); LOGGER.info("find id = {}", id);
try { try {
return DataAccess.get(Data.class, id); return DataAccess.get(Data.class, id);
} catch (Exception e) { } catch (final Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
} }
public static Data createNewData(long tmpUID, String originalFileName, String sha512) throws IOException { public static Data createNewData(final long tmpUID, final String originalFileName, final String sha512) throws IOException {
// determine mime type: // determine mime type:
Data injectedData = new Data(); Data injectedData = new Data();
String mimeType = ""; String mimeType = "";
String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1); final String extension = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
switch (extension.toLowerCase()) { mimeType = switch (extension.toLowerCase()) {
case "jpg": case "jpg", "jpeg" -> "image/jpeg";
case "jpeg": case "png" -> "image/png";
mimeType = "image/jpeg"; case "webp" -> "image/webp";
break; case "mka" -> "audio/x-matroska";
case "png": case "mkv" -> "video/x-matroska";
mimeType = "image/png"; case "webm" -> "video/webm";
break; default -> throw new IOException("Can not find the mime type of data input: '" + extension + "'");
case "webp": };
mimeType = "image/webp";
break;
case "mka":
mimeType = "audio/x-matroska";
break;
case "mkv":
mimeType = "video/x-matroska";
break;
case "webm":
mimeType = "video/webm";
break;
default:
throw new IOException("Can not find the mime type of data input: '" + extension + "'");
}
injectedData.mimeType = mimeType; injectedData.mimeType = mimeType;
injectedData.sha512 = sha512; injectedData.sha512 = sha512;
String tmpPath = getTmpFileInData(tmpUID); final String tmpPath = getTmpFileInData(tmpUID);
injectedData.size = Files.size(Paths.get(tmpPath)); injectedData.size = Files.size(Paths.get(tmpPath));
try { try {
injectedData = DataAccess.insert(injectedData); injectedData = DataAccess.insert(injectedData);
} catch (Exception e) { } catch (final Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
String mediaPath = getFileData(injectedData.id); final String mediaPath = getFileData(injectedData.id);
LOGGER.info("src = {}", tmpPath); LOGGER.info("src = {}", tmpPath);
LOGGER.info("dst = {}", mediaPath); LOGGER.info("dst = {}", mediaPath);
Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE); Files.move(Paths.get(tmpPath), Paths.get(mediaPath), StandardCopyOption.ATOMIC_MOVE);
@ -163,16 +149,16 @@ public class DataResource {
return injectedData; return injectedData;
} }
public static String saveTemporaryFile(InputStream uploadedInputStream, long idData) { public static String saveTemporaryFile(final InputStream uploadedInputStream, final long idData) {
return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData)); return saveFile(uploadedInputStream, DataResource.getTmpFileInData(idData));
} }
public static void removeTemporaryFile(long idData) { public static void removeTemporaryFile(final long idData) {
String filepath = DataResource.getTmpFileInData(idData); final String filepath = DataResource.getTmpFileInData(idData);
if (Files.exists(Paths.get(filepath))) { if (Files.exists(Paths.get(filepath))) {
try { try {
Files.delete(Paths.get(filepath)); Files.delete(Paths.get(filepath));
} catch (IOException e) { } catch (final IOException e) {
LOGGER.info("can not delete temporary file : {}", Paths.get(filepath)); LOGGER.info("can not delete temporary file : {}", Paths.get(filepath));
e.printStackTrace(); e.printStackTrace();
} }
@ -180,13 +166,13 @@ public class DataResource {
} }
// save uploaded file to a defined location on the server // save uploaded file to a defined location on the server
static String saveFile(InputStream uploadedInputStream, String serverLocation) { static String saveFile(final InputStream uploadedInputStream, final String serverLocation) {
String out = ""; String out = "";
try { try {
OutputStream outpuStream = new FileOutputStream(new File(serverLocation)); OutputStream outpuStream = new FileOutputStream(new File(serverLocation));
int read = 0; int read = 0;
byte[] bytes = new byte[CHUNK_SIZE_IN]; final byte[] bytes = new byte[CHUNK_SIZE_IN];
MessageDigest md = MessageDigest.getInstance("SHA-512"); final MessageDigest md = MessageDigest.getInstance("SHA-512");
outpuStream = new FileOutputStream(new File(serverLocation)); outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) { while ((read = uploadedInputStream.read(bytes)) != -1) {
@ -199,32 +185,32 @@ public class DataResource {
outpuStream.flush(); outpuStream.flush();
outpuStream.close(); outpuStream.close();
// create the end of sha512 // create the end of sha512
byte[] sha512Digest = md.digest(); final byte[] sha512Digest = md.digest();
// convert in hexadecimal // convert in hexadecimal
out = bytesToHex(sha512Digest); out = bytesToHex(sha512Digest);
uploadedInputStream.close(); uploadedInputStream.close();
} catch (IOException ex) { } catch (final IOException ex) {
LOGGER.info("Can not write in temporary file ... "); LOGGER.info("Can not write in temporary file ... ");
ex.printStackTrace(); ex.printStackTrace();
} catch (NoSuchAlgorithmException ex) { } catch (final NoSuchAlgorithmException ex) {
LOGGER.info("Can not find sha512 algorithms"); LOGGER.info("Can not find sha512 algorithms");
ex.printStackTrace(); ex.printStackTrace();
} }
return out; return out;
} }
public static String bytesToHex(byte[] bytes) { public static String bytesToHex(final byte[] bytes) {
StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
for (byte b : bytes) { for (final byte b : bytes) {
sb.append(String.format("%02x", b)); sb.append(String.format("%02x", b));
} }
return sb.toString(); return sb.toString();
} }
public Data getSmall(Long id) { public Data getSmall(final Long id) {
try { try {
return DataAccess.get(Data.class, id); return DataAccess.get(Data.class, id);
} catch (Exception e) { } catch (final Exception e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
} }
@ -235,17 +221,17 @@ public class DataResource {
@Path("/upload/") @Path("/upload/")
@Consumes({ MediaType.MULTIPART_FORM_DATA }) @Consumes({ MediaType.MULTIPART_FORM_DATA })
@RolesAllowed("ADMIN") @RolesAllowed("ADMIN")
public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) { public Response uploadFile(@Context final SecurityContext sc, @FormDataParam("file") final InputStream fileInputStream, @FormDataParam("file") final FormDataContentDisposition fileMetaData) {
GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
LOGGER.info("==================================================="); LOGGER.info("===================================================");
LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken)); LOGGER.info("== DATA uploadFile {}", (gc == null ? "null" : gc.userByToken));
LOGGER.info("==================================================="); LOGGER.info("===================================================");
//public NodeSmall uploadFile(final FormDataMultiPart form) { //public NodeSmall uploadFile(final FormDataMultiPart form) {
LOGGER.info("Upload file: "); LOGGER.info("Upload file: ");
String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++; final String filePath = ConfigBaseVariable.getTmpDataFolder() + File.separator + tmpFolderId++;
try { try {
createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator); createFolder(ConfigBaseVariable.getTmpDataFolder() + File.separator);
} catch (IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
saveFile(fileInputStream, filePath); saveFile(fileInputStream, filePath);
@ -258,12 +244,13 @@ public class DataResource {
@PermitTokenInURI @PermitTokenInURI
@RolesAllowed("USER") @RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception { public Response retriveDataId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
GenericContext gc = (GenericContext) sc.getUserPrincipal(); @PathParam("id") final Long id) throws Exception {
final GenericContext gc = (GenericContext) sc.getUserPrincipal();
//logger.info("==================================================="); //logger.info("===================================================");
LOGGER.info("== DATA retriveDataId ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); LOGGER.info("== DATA retriveDataId ? id={} user={}", id, (gc == null ? "null" : gc.userByToken));
//logger.info("==================================================="); //logger.info("===================================================");
Data value = getSmall(id); final Data value = getSmall(id);
if (value == null) { if (value == null) {
Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
} }
@ -276,18 +263,18 @@ public class DataResource {
@PermitTokenInURI @PermitTokenInURI
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
//@CacheMaxAge(time = 10, unit = TimeUnit.DAYS) //@CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retriveDataThumbnailId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) public Response retriveDataThumbnailId(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
throws Exception { @PathParam("id") final Long id) throws Exception {
//GenericContext gc = (GenericContext) sc.getUserPrincipal(); //GenericContext gc = (GenericContext) sc.getUserPrincipal();
//logger.info("==================================================="); //logger.info("===================================================");
//logger.info("== DATA retriveDataThumbnailId ? {}", (gc==null?"null":gc.user)); //logger.info("== DATA retriveDataThumbnailId ? {}", (gc==null?"null":gc.user));
//logger.info("==================================================="); //logger.info("===================================================");
Data value = getSmall(id); final Data value = getSmall(id);
if (value == null) { if (value == null) {
return Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); return Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
} }
String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data"; final String filePathName = ConfigBaseVariable.getMediaDataFolder() + File.separator + id + File.separator + "data";
File inputFile = new File(filePathName); final File inputFile = new File(filePathName);
if (!inputFile.exists()) { if (!inputFile.exists()) {
return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build(); return Response.status(404).entity("{\"error\":\"media Does not exist: " + id + "\"}").type("application/json").build();
} }
@ -295,31 +282,31 @@ public class DataResource {
// || value.mimeType.contentEquals("image/webp") // || value.mimeType.contentEquals("image/webp")
) { ) {
// reads input image // reads input image
BufferedImage inputImage = ImageIO.read(inputFile); final BufferedImage inputImage = ImageIO.read(inputFile);
int scaledWidth = 250; final int scaledWidth = 250;
int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth() * (float) scaledWidth); final int scaledHeight = (int) ((float) inputImage.getHeight() / (float) inputImage.getWidth() * scaledWidth);
// creates output image // creates output image
BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType()); final BufferedImage outputImage = new BufferedImage(scaledWidth, scaledHeight, inputImage.getType());
// scales the input image to the output image // scales the input image to the output image
Graphics2D g2d = outputImage.createGraphics(); final Graphics2D g2d = outputImage.createGraphics();
g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null); g2d.drawImage(inputImage, 0, 0, scaledWidth, scaledHeight, null);
g2d.dispose(); g2d.dispose();
// create the output stream: // create the output stream:
ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try { try {
// TODO: check how to remove buffer file !!! here, it is not needed at all... // TODO: check how to remove buffer file !!! here, it is not needed at all...
ImageIO.write(outputImage, "JPG", baos); ImageIO.write(outputImage, "JPG", baos);
} catch (IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain").build(); return Response.status(500).entity("Internal Error: resize fail: " + e.getMessage()).type("text/plain").build();
} }
byte[] imageData = baos.toByteArray(); final byte[] imageData = baos.toByteArray();
//Response.ok(new ByteArrayInputStream(imageData)).build(); //Response.ok(new ByteArrayInputStream(imageData)).build();
Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH, imageData.length); final Response.ResponseBuilder out = Response.ok(imageData).header(HttpHeaders.CONTENT_LENGTH, imageData.length);
out.type("image/jpeg"); out.type("image/jpeg");
// TODO: move this in a decorator !!! // TODO: move this in a decorator !!!
CacheControl cc = new CacheControl(); final CacheControl cc = new CacheControl();
cc.setMaxAge(3600); cc.setMaxAge(3600);
cc.setNoCache(false); cc.setNoCache(false);
out.cacheControl(cc); out.cacheControl(cc);
@ -334,13 +321,13 @@ public class DataResource {
@PermitTokenInURI @PermitTokenInURI
@RolesAllowed("USER") @RolesAllowed("USER")
@Produces(MediaType.APPLICATION_OCTET_STREAM) @Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response retriveDataFull(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id, public Response retriveDataFull(@Context final SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) final String token, @HeaderParam("Range") final String range,
@PathParam("name") String name) throws Exception { @PathParam("id") final Long id, @PathParam("name") final String name) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal(); final GenericContext gc = (GenericContext) sc.getUserPrincipal();
//logger.info("==================================================="); //logger.info("===================================================");
LOGGER.info("== DATA retriveDataFull ? id={} user={}", id, (gc == null ? "null" : gc.userByToken)); LOGGER.info("== DATA retriveDataFull ? id={} user={}", id, (gc == null ? "null" : gc.userByToken));
//logger.info("==================================================="); //logger.info("===================================================");
Data value = getSmall(id); final Data value = getSmall(id);
if (value == null) { if (value == null) {
Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build(); Response.status(404).entity("media NOT FOUND: " + id).type("text/plain").build();
} }
@ -354,33 +341,33 @@ public class DataResource {
* @return Streaming output * @return Streaming output
* @throws Exception IOException if an error occurs in streaming. * @throws Exception IOException if an error occurs in streaming.
*/ */
private Response buildStream(final String filename, final String range, String mimeType) throws Exception { private Response buildStream(final String filename, final String range, final String mimeType) throws Exception {
File file = new File(filename); final File file = new File(filename);
//logger.info("request range : {}", range); //logger.info("request range : {}", range);
// range not requested : Firefox does not send range headers // range not requested : Firefox does not send range headers
if (range == null) { if (range == null) {
final StreamingOutput output = new StreamingOutput() { final StreamingOutput output = new StreamingOutput() {
@Override @Override
public void write(OutputStream out) { public void write(final OutputStream out) {
try (FileInputStream in = new FileInputStream(file)) { try (FileInputStream in = new FileInputStream(file)) {
byte[] buf = new byte[1024 * 1024]; final byte[] buf = new byte[1024 * 1024];
int len; int len;
while ((len = in.read(buf)) != -1) { while ((len = in.read(buf)) != -1) {
try { try {
out.write(buf, 0, len); out.write(buf, 0, len);
out.flush(); out.flush();
//logger.info("---- wrote {} bytes file ----", len); //logger.info("---- wrote {} bytes file ----", len);
} catch (IOException ex) { } catch (final IOException ex) {
LOGGER.info("remote close connection"); LOGGER.info("remote close connection");
break; break;
} }
} }
} catch (IOException ex) { } catch (final IOException ex) {
throw new InternalServerErrorException(ex); throw new InternalServerErrorException(ex);
} }
} }
}; };
Response.ResponseBuilder out = Response.ok(output).header(HttpHeaders.CONTENT_LENGTH, file.length()); final Response.ResponseBuilder out = Response.ok(output).header(HttpHeaders.CONTENT_LENGTH, file.length());
if (mimeType != null) { if (mimeType != null) {
out.type(mimeType); out.type(mimeType);
} }
@ -388,7 +375,7 @@ public class DataResource {
} }
String[] ranges = range.split("=")[1].split("-"); final String[] ranges = range.split("=")[1].split("-");
final long from = Long.parseLong(ranges[0]); final long from = Long.parseLong(ranges[0]);
//logger.info("request range : {}", ranges.length); //logger.info("request range : {}", ranges.length);
@ -396,10 +383,8 @@ public class DataResource {
long to = CHUNK_SIZE + from; long to = CHUNK_SIZE + from;
if (ranges.length == 1) { if (ranges.length == 1) {
to = file.length() - 1; to = file.length() - 1;
} else { } else if (to >= file.length()) {
if (to >= file.length()) { to = file.length() - 1;
to = (long) (file.length() - 1);
}
} }
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length()); final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
//logger.info("responseRange: {}", responseRange); //logger.info("responseRange: {}", responseRange);
@ -408,7 +393,7 @@ public class DataResource {
final long len = to - from + 1; final long len = to - from + 1;
final MediaStreamer streamer = new MediaStreamer(len, raf); final MediaStreamer streamer = new MediaStreamer(len, raf);
Response.ResponseBuilder out = Response.ok(streamer).status(Response.Status.PARTIAL_CONTENT).header("Accept-Ranges", "bytes").header("Content-Range", responseRange) final Response.ResponseBuilder out = Response.ok(streamer).status(Response.Status.PARTIAL_CONTENT).header("Accept-Ranges", "bytes").header("Content-Range", responseRange)
.header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth()).header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified())); .header(HttpHeaders.CONTENT_LENGTH, streamer.getLenth()).header(HttpHeaders.LAST_MODIFIED, new Date(file.lastModified()));
if (mimeType != null) { if (mimeType != null) {
out.type(mimeType); out.type(mimeType);
@ -416,7 +401,7 @@ public class DataResource {
return out.build(); return out.build();
} }
public static void undelete(Long id) throws Exception { public static void undelete(final Long id) throws Exception {
DataAccess.unsetDelete(Data.class, id); DataAccess.unsetDelete(Data.class, id);
} }

View File

@ -3,10 +3,10 @@ package org.kar.archidata.api;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import org.kar.archidata.annotation.security.PermitAll;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import jakarta.annotation.security.PermitAll;
import jakarta.ws.rs.GET; import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException; import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.NotSupportedException; import jakarta.ws.rs.NotSupportedException;
@ -22,16 +22,16 @@ public class FrontGeneric {
protected String baseFrontFolder = "/data/front"; protected String baseFrontFolder = "/data/front";
private String getExtension(String filename) { private String getExtension(final String filename) {
if (filename.contains(".")) { if (filename.contains(".")) {
return filename.substring(filename.lastIndexOf(".") + 1); return filename.substring(filename.lastIndexOf(".") + 1);
} }
return ""; return "";
} }
private Response retrive(String fileName) throws Exception { private Response retrive(final String fileName) throws Exception {
String filePathName = baseFrontFolder + File.separator + fileName; String filePathName = this.baseFrontFolder + File.separator + fileName;
String extention = getExtension(filePathName); final String extention = getExtension(filePathName);
String mineType = null; String mineType = null;
LOGGER.debug("try retrive : '{}' '{}'", filePathName, extention); LOGGER.debug("try retrive : '{}' '{}'", filePathName, extention);
if (extention.length() != 0 && extention.length() <= 5) { if (extention.length() != 0 && extention.length() <= 5) {
@ -66,18 +66,18 @@ public class FrontGeneric {
} }
} else { } else {
mineType = "text/html"; mineType = "text/html";
filePathName = baseFrontFolder + File.separator + "index.html"; filePathName = this.baseFrontFolder + File.separator + "index.html";
} }
LOGGER.debug(" ==> '[}'", filePathName); LOGGER.debug(" ==> '[}'", filePathName);
// reads input image // reads input image
File download = new File(filePathName); final File download = new File(filePathName);
if (!download.exists()) { if (!download.exists()) {
throw new NotFoundException("Not Found: '" + fileName + "' extension='" + extention + "'"); throw new NotFoundException("Not Found: '" + fileName + "' extension='" + extention + "'");
} }
ResponseBuilder response = Response.ok((Object) download); final ResponseBuilder response = Response.ok(download);
// use this if I want to download the file: // use this if I want to download the file:
//response.header("Content-Disposition", "attachment; filename=" + fileName); //response.header("Content-Disposition", "attachment; filename=" + fileName);
CacheControl cc = new CacheControl(); final CacheControl cc = new CacheControl();
cc.setMaxAge(60); cc.setMaxAge(60);
cc.setNoCache(false); cc.setNoCache(false);
response.cacheControl(cc); response.cacheControl(cc);
@ -99,9 +99,9 @@ public class FrontGeneric {
@PermitAll() @PermitAll()
//@Produces(MediaType.APPLICATION_OCTET_STREAM) //@Produces(MediaType.APPLICATION_OCTET_STREAM)
//@CacheMaxAge(time = 10, unit = TimeUnit.DAYS) //@CacheMaxAge(time = 10, unit = TimeUnit.DAYS)
public Response retrive1(@PathParam("any") List<PathSegment> segments) throws Exception { public Response retrive1(@PathParam("any") final List<PathSegment> segments) throws Exception {
String filename = ""; String filename = "";
for (PathSegment elem : segments) { for (final PathSegment elem : segments) {
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
filename += File.separator; filename += File.separator;
} }

View File

@ -14,11 +14,11 @@ import jakarta.ws.rs.core.StreamingOutput;
public class MediaStreamer implements StreamingOutput { public class MediaStreamer implements StreamingOutput {
private static final Logger LOGGER = LoggerFactory.getLogger(MediaStreamer.class); private static final Logger LOGGER = LoggerFactory.getLogger(MediaStreamer.class);
private final int CHUNK_SIZE = 1024 * 1024; // 1MB chunks private final int CHUNK_SIZE = 1024 * 1024; // 1MB chunks
final byte[] buf = new byte[CHUNK_SIZE]; final byte[] buf = new byte[this.CHUNK_SIZE];
private long length; private long length;
private RandomAccessFile raf; private final RandomAccessFile raf;
public MediaStreamer(long length, RandomAccessFile raf) throws IOException { public MediaStreamer(final long length, final RandomAccessFile raf) throws IOException {
//logger.info("request stream of {} data", length / 1024); //logger.info("request stream of {} data", length / 1024);
if (length < 0) { if (length < 0) {
throw new IOException("Wrong size of the file to stream: " + length); throw new IOException("Wrong size of the file to stream: " + length);
@ -28,26 +28,26 @@ public class MediaStreamer implements StreamingOutput {
} }
@Override @Override
public void write(OutputStream outputStream) { public void write(final OutputStream outputStream) {
try { try {
while (length != 0) { while (this.length != 0) {
int read = raf.read(buf, 0, buf.length > length ? (int) length : buf.length); final int read = this.raf.read(this.buf, 0, this.buf.length > this.length ? (int) this.length : this.buf.length);
try { try {
outputStream.write(buf, 0, read); outputStream.write(this.buf, 0, read);
} catch (IOException ex) { } catch (final IOException ex) {
LOGGER.info("remote close connection"); LOGGER.info("remote close connection");
break; break;
} }
length -= read; this.length -= read;
} }
} catch (IOException ex) { } catch (final IOException ex) {
throw new InternalServerErrorException(ex); throw new InternalServerErrorException(ex);
} catch (WebApplicationException ex) { } catch (final WebApplicationException ex) {
throw new InternalServerErrorException(ex); throw new InternalServerErrorException(ex);
} finally { } finally {
try { try {
raf.close(); this.raf.close();
} catch (IOException ex) { } catch (final IOException ex) {
ex.printStackTrace(); ex.printStackTrace();
throw new InternalServerErrorException(ex); throw new InternalServerErrorException(ex);
} }
@ -55,7 +55,7 @@ public class MediaStreamer implements StreamingOutput {
} }
public long getLenth() { public long getLenth() {
return length; return this.length;
} }
} }

View File

@ -623,7 +623,7 @@ public class DataAccess {
// check the compatibility of the id and the declared ID // check the compatibility of the id and the declared ID
final Class<?> typeClass = idField.getType(); final Class<?> typeClass = idField.getType();
if (id == typeClass) { if (id == typeClass) {
throw new Exception("Request update with the wriong type ..."); throw new Exception("Request update with the wrong type ...");
} }
// Udpade Json Value // Udpade Json Value
return updateWithJson(clazz, new QueryCondition(AnnotationTools.getFieldName(idField), "=", id), jsonData); return updateWithJson(clazz, new QueryCondition(AnnotationTools.getFieldName(idField), "=", id), jsonData);
@ -638,15 +638,15 @@ 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 update(data, condition, keys); return updateWhere(data, condition, null, 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 {
return update(data, id, null); return update(data, id, null);
} }
public static <T> int update(final T data, final QueryItem condition) throws Exception { public static <T> int updateWhere(final T data, final QueryItem condition) throws Exception {
return update(data, condition, null); return updateWhere(data, condition, null, null);
} }
/** /**

View File

@ -10,10 +10,7 @@ import java.util.Map.Entry;
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178 // https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178
// https://stackoverflow.com/questions/32817210/how-to-access-jersey-resource-secured-by-rolesallowed // https://stackoverflow.com/questions/32817210/how-to-access-jersey-resource-secured-by-rolesallowed
import org.kar.archidata.annotation.security.DenyAll;
import org.kar.archidata.annotation.security.PermitAll;
import org.kar.archidata.annotation.security.PermitTokenInURI; import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.annotation.security.RolesAllowed;
import org.kar.archidata.catcher.RestErrorResponse; import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import org.kar.archidata.util.JWTWrapper; import org.kar.archidata.util.JWTWrapper;
@ -23,6 +20,9 @@ import org.slf4j.LoggerFactory;
import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet;
import jakarta.annotation.Priority; import jakarta.annotation.Priority;
import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Priorities; import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ContainerRequestFilter;
@ -46,23 +46,22 @@ public class AuthenticationFilter implements ContainerRequestFilter {
private static final String AUTHENTICATION_SCHEME = "Yota"; private static final String AUTHENTICATION_SCHEME = "Yota";
private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota"; private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota";
public AuthenticationFilter(String applicationName) { public AuthenticationFilter(final String applicationName) {
super();
this.applicationName = applicationName; this.applicationName = applicationName;
} }
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(final ContainerRequestContext requestContext) throws IOException {
/* /*
logger.debug("-----------------------------------------------------"); logger.debug("-----------------------------------------------------");
logger.debug("---- Check if have authorization ----"); logger.debug("---- Check if have authorization ----");
logger.debug("-----------------------------------------------------"); logger.debug("-----------------------------------------------------");
logger.debug(" for:{}", requestContext.getUriInfo().getPath()); logger.debug(" for:{}", requestContext.getUriInfo().getPath());
*/ */
Method method = 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)) {
logger.debug(" ==> deny all {}", requestContext.getUriInfo().getPath()); this.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;
} }
@ -75,7 +74,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)) {
logger.error(" ==> missing @RolesAllowed {}", requestContext.getUriInfo().getPath()); this.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;
} }
@ -84,8 +83,8 @@ public class AuthenticationFilter implements ContainerRequestFilter {
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
//logger.debug("authorizationHeader: {}", authorizationHeader); //logger.debug("authorizationHeader: {}", authorizationHeader);
if (authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) { if (authorizationHeader == null && method.isAnnotationPresent(PermitTokenInURI.class)) {
MultivaluedMap<String, String> quaryparam = requestContext.getUriInfo().getQueryParameters(); final MultivaluedMap<String, String> quaryparam = requestContext.getUriInfo().getQueryParameters();
for (Entry<String, List<String>> item : quaryparam.entrySet()) { for (final Entry<String, List<String>> item : quaryparam.entrySet()) {
if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) { if (item.getKey().equals(HttpHeaders.AUTHORIZATION)) {
if (!item.getValue().isEmpty()) { if (!item.getValue().isEmpty()) {
authorizationHeader = item.getValue().get(0); authorizationHeader = item.getValue().get(0);
@ -95,58 +94,58 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
} }
// logger.debug("authorizationHeader: {}", authorizationHeader); // logger.debug("authorizationHeader: {}", authorizationHeader);
boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader); final boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader);
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) {
logger.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath()); this.logger.warn("REJECTED unauthorized: {}", requestContext.getUriInfo().getPath());
abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath()); abortWithUnauthorized(requestContext, "REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
return; return;
} }
UserByToken userByToken = null; UserByToken userByToken = null;
if (isJwtToken) { if (isJwtToken) {
// Extract the token from the Authorization header (Remove "Yota ") // Extract the token from the Authorization header (Remove "Yota ")
String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim(); final String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
//logger.debug("token: {}", token); //logger.debug("token: {}", token);
try { try {
userByToken = validateJwtToken(token); userByToken = validateJwtToken(token);
} catch (Exception e) { } catch (final Exception e) {
logger.error("Fail to validate token: {}", e.getMessage()); this.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) {
logger.warn("get a NULL user ..."); this.logger.warn("get a NULL user ...");
abortWithUnauthorized(requestContext, "get a NULL user ..."); abortWithUnauthorized(requestContext, "get a NULL user ...");
return; return;
} }
} else { } else {
// Extract the token from the Authorization header (Remove "Zota ") // Extract the token from the Authorization header (Remove "Zota ")
String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim(); final String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim();
//logger.debug("token: {}", token); //logger.debug("token: {}", token);
try { try {
userByToken = validateToken(token); userByToken = validateToken(token);
} catch (Exception e) { } catch (final Exception e) {
logger.error("Fail to validate token: {}", e.getMessage()); this.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) {
logger.warn("get a NULL application ..."); this.logger.warn("get a NULL application ...");
abortWithUnauthorized(requestContext, "get a NULL application ..."); abortWithUnauthorized(requestContext, "get a NULL application ...");
return; return;
} }
} }
// create the security context model: // create the security context model:
String scheme = requestContext.getUriInfo().getRequestUri().getScheme(); final String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
MySecurityContext userContext = new MySecurityContext(userByToken, scheme); final MySecurityContext userContext = new MySecurityContext(userByToken, scheme);
// retrieve the allowed right: // retrieve the allowed right:
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class); final RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
List<String> roles = Arrays.asList(rolesAnnotation.value()); final List<String> roles = Arrays.asList(rolesAnnotation.value());
// check if the user have the right: // check if the user have the right:
boolean haveRight = false; boolean haveRight = false;
for (String role : roles) { for (final String role : roles) {
if (userContext.isUserInRole(role)) { if (userContext.isUserInRole(role)) {
haveRight = true; haveRight = true;
break; break;
@ -154,7 +153,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
//Is user valid? //Is user valid?
if (!haveRight) { if (!haveRight) {
logger.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles); this.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;
} }
@ -162,59 +161,59 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// logger.debug("Get local user : {} / {}", user, userByToken); // logger.debug("Get local user : {} / {}", user, userByToken);
} }
private boolean isTokenBasedAuthentication(String authorizationHeader) { private boolean isTokenBasedAuthentication(final String authorizationHeader) {
// Check if the Authorization header is valid // Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace // It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive // The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "); return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
} }
private boolean isApplicationTokenBasedAuthentication(String authorizationHeader) { private boolean isApplicationTokenBasedAuthentication(final String authorizationHeader) {
// Check if the Authorization header is valid // Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace // It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive // The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_TOKEN_SCHEME.toLowerCase() + " "); return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_TOKEN_SCHEME.toLowerCase() + " ");
} }
private void abortWithUnauthorized(ContainerRequestContext requestContext, String message) { private void abortWithUnauthorized(final ContainerRequestContext requestContext, final String message) {
// 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
logger.warn("abortWithUnauthorized:"); this.logger.warn("abortWithUnauthorized:");
RestErrorResponse ret = new RestErrorResponse(Response.Status.UNAUTHORIZED, "Unauthorized", message); final RestErrorResponse ret = new RestErrorResponse(Response.Status.UNAUTHORIZED, "Unauthorized", message);
logger.error("Error UUID={}", ret.uuid); this.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(String authorization) throws Exception { protected UserByToken validateToken(final String authorization) throws Exception {
logger.info("Must be Override by the application implmentation, otherwise it dose not work"); this.logger.info("Must be Override by the application implmentation, otherwise it dose not work");
return null; return null;
} }
// must be override to be good implementation // must be override to be good implementation
protected UserByToken validateJwtToken(String authorization) throws Exception { protected UserByToken validateJwtToken(final String authorization) throws Exception {
//logger.debug(" validate token : " + authorization); //logger.debug(" validate token : " + authorization);
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) {
logger.error("The token is not valid: '{}'", authorization); this.logger.error("The token is not valid: '{}'", authorization);
return null; return null;
} }
// check userID // check userID
String userUID = ret.getSubject(); final String userUID = ret.getSubject();
long id = Long.parseLong(userUID); final long id = Long.parseLong(userUID);
UserByToken user = new UserByToken(); final UserByToken user = new UserByToken();
user.id = id; user.id = id;
user.name = (String) ret.getClaim("login"); user.name = (String) ret.getClaim("login");
user.type = UserByToken.TYPE_USER; user.type = UserByToken.TYPE_USER;
Object rowRight = ret.getClaim("right"); final Object rowRight = ret.getClaim("right");
if (rowRight != null) { if (rowRight != null) {
Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right"); final Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right");
if (rights.containsKey(this.applicationName)) { if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName); user.right = rights.get(this.applicationName);
} else { } else {
logger.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights); this.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

@ -4,6 +4,4 @@ import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public record GetToken( public record GetToken(
String jwt) { String jwt) {}
}

View File

@ -13,6 +13,7 @@ public class ConfigBaseVariable {
static public String apiAdress; static public String apiAdress;
static public String ssoAdress; static public String ssoAdress;
static public String ssoToken; static public String ssoToken;
static public String testMode;
// For test only // For test only
public static void clearAllValue() { public static void clearAllValue() {
@ -28,6 +29,7 @@ public class ConfigBaseVariable {
apiAdress = System.getenv("API_ADDRESS"); apiAdress = System.getenv("API_ADDRESS");
ssoAdress = System.getenv("SSO_ADDRESS"); ssoAdress = System.getenv("SSO_ADDRESS");
ssoToken = System.getenv("SSO_TOKEN"); ssoToken = System.getenv("SSO_TOKEN");
testMode = System.getenv("TEST_MODE");
} }
static { static {
@ -111,4 +113,11 @@ public class ConfigBaseVariable {
public static String ssoToken() { public static String ssoToken() {
return ssoToken; return ssoToken;
} }
public static boolean getTestMode() {
if (testMode == null) {
return false;
}
return Boolean.parseBoolean(testMode);
}
} }

View File

@ -8,6 +8,7 @@ import java.net.URL;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -18,17 +19,57 @@ import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType; import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier;
import com.nimbusds.jose.jca.JCAContext;
import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.gen.RSAKeyGenerator; import com.nimbusds.jose.jwk.gen.RSAKeyGenerator;
import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.SignedJWT;
class TestSigner implements JWSSigner {
public static String test_signature = "TEST_SIGNATURE_FOR_LOCAL_TEST_AND_TEST_E2E";
/**
* Signs the specified {@link JWSObject#getSigningInput input} of a
* {@link JWSObject JWS object}.
*
* @param header The JSON Web Signature (JWS) header. Must
* specify a supported JWS algorithm and must not
* be {@code null}.
* @param signingInput The input to sign. Must not be {@code null}.
*
* @return The resulting signature part (third part) of the JWS object.
*
* @throws JOSEException If the JWS algorithm is not supported, if a
* critical header parameter is not supported or
* marked for deferral to the application, or if
* signing failed for some other internal reason.
*/
@Override
public Base64URL sign(final JWSHeader header, final byte[] signingInput) throws JOSEException {
return new Base64URL(test_signature);
}
@Override
public Set<JWSAlgorithm> supportedJWSAlgorithms() {
// TODO Auto-generated method stub
return null;
}
@Override
public JCAContext getJCAContext() {
// TODO Auto-generated method stub
return null;
}
}
public class JWTWrapper { public class JWTWrapper {
static final Logger logger = LoggerFactory.getLogger(JWTWrapper.class); static final Logger LOGGER = LoggerFactory.getLogger(JWTWrapper.class);
private static RSAKey rsaJWK = null;; private static RSAKey rsaJWK = null;;
private static RSAKey rsaPublicJWK = null; private static RSAKey rsaPublicJWK = null;
@ -36,79 +77,79 @@ public class JWTWrapper {
public static class PublicKey { public static class PublicKey {
public String key; public String key;
public PublicKey(String key) { public PublicKey(final String key) {
this.key = key; this.key = key;
} }
public PublicKey() {} public PublicKey() {}
} }
public static void initLocalTokenRemote(String ssoUri, String application) throws IOException, ParseException { public static void initLocalTokenRemote(final String ssoUri, final String application) throws IOException, ParseException {
// check Token: // check Token:
URL obj = new URL(ssoUri + "public_key"); final URL obj = new URL(ssoUri + "public_key");
//logger.debug("Request token from: {}", obj); //LOGGER.debug("Request token from: {}", obj);
HttpURLConnection con = (HttpURLConnection) obj.openConnection(); final HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET"); con.setRequestMethod("GET");
con.setRequestProperty("User-Agent", application); con.setRequestProperty("User-Agent", application);
con.setRequestProperty("Cache-Control", "no-cache"); con.setRequestProperty("Cache-Control", "no-cache");
con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json"); con.setRequestProperty("Accept", "application/json");
String ssoToken = ConfigBaseVariable.ssoToken(); final String ssoToken = ConfigBaseVariable.ssoToken();
if (ssoToken != null) { if (ssoToken != null) {
con.setRequestProperty("Authorization", "Zota " + ssoToken); con.setRequestProperty("Authorization", "Zota " + ssoToken);
} }
int responseCode = con.getResponseCode(); final int responseCode = con.getResponseCode();
//logger.debug("GET Response Code :: {}", responseCode); //LOGGER.debug("GET Response Code :: {}", responseCode);
if (responseCode == HttpURLConnection.HTTP_OK) { // success if (responseCode == HttpURLConnection.HTTP_OK) { // success
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); final BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine; String inputLine;
StringBuffer response = new StringBuffer(); final StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) { while ((inputLine = in.readLine()) != null) {
response.append(inputLine); response.append(inputLine);
} }
in.close(); in.close();
// print result // print result
//logger.debug(response.toString()); //LOGGER.debug(response.toString());
ObjectMapper mapper = new ObjectMapper(); final ObjectMapper mapper = new ObjectMapper();
PublicKey values = mapper.readValue(response.toString(), PublicKey.class); final PublicKey values = mapper.readValue(response.toString(), PublicKey.class);
rsaPublicJWK = RSAKey.parse(values.key); rsaPublicJWK = RSAKey.parse(values.key);
return; return;
} }
logger.debug("GET JWT validator token not worked response code {} from {} ", responseCode, obj); LOGGER.debug("GET JWT validator token not worked response code {} from {} ", responseCode, obj);
} }
public static void initLocalToken(String baseUUID) throws Exception { public static void initLocalToken(final String baseUUID) throws Exception {
// RSA signatures require a public and private RSA key pair, the public key // RSA signatures require a public and private RSA key pair, the public key
// must be made known to the JWS recipient in order to verify the signatures // must be made known to the JWS recipient in order to verify the signatures
try { try {
String generatedStringForKey = baseUUID; String generatedStringForKey = baseUUID;
if (generatedStringForKey == null) { if (generatedStringForKey == null) {
logger.error(" Generate new UUID : {}", generatedStringForKey); LOGGER.error(" Generate new UUID : {}", generatedStringForKey);
generatedStringForKey = UUID.randomUUID().toString(); generatedStringForKey = UUID.randomUUID().toString();
} else { } else {
logger.error("USE UUID : {}", generatedStringForKey); LOGGER.error("USE UUID : {}", generatedStringForKey);
} }
rsaJWK = new RSAKeyGenerator(2048).keyID(generatedStringForKey).generate(); rsaJWK = new RSAKeyGenerator(2048).keyID(generatedStringForKey).generate();
rsaPublicJWK = rsaJWK.toPublicJWK(); rsaPublicJWK = rsaJWK.toPublicJWK();
logger.error("RSA key (all): " + rsaJWK.toJSONString()); LOGGER.error("RSA key (all): " + rsaJWK.toJSONString());
logger.error("RSA key (pub): " + rsaPublicJWK.toJSONString()); LOGGER.error("RSA key (pub): " + rsaPublicJWK.toJSONString());
} catch (JOSEException e) { } catch (final JOSEException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); e.printStackTrace();
logger.debug("Can not generate teh public abnd private keys ..."); LOGGER.debug("Can not generate teh public abnd private keys ...");
rsaJWK = null; rsaJWK = null;
rsaPublicJWK = null; rsaPublicJWK = null;
} }
} }
public static void initValidateToken(String publicKey) { public static void initValidateToken(final String publicKey) {
try { try {
rsaPublicJWK = RSAKey.parse(publicKey); rsaPublicJWK = RSAKey.parse(publicKey);
} catch (ParseException e) { } catch (final ParseException e) {
e.printStackTrace(); e.printStackTrace();
logger.debug("Can not retrieve public Key !!!!!!!! RSAKey='{}'", publicKey); LOGGER.debug("Can not retrieve public Key !!!!!!!! RSAKey='{}'", publicKey);
} }
} }
@ -136,81 +177,122 @@ public class JWTWrapper {
* @param timeOutInMunites Expiration of the token. * @param timeOutInMunites Expiration of the token.
* @return the encoded token * @return the encoded token
*/ */
public static String generateJWToken(long userID, String userLogin, String isuer, String application, Map<String, Object> rights, int timeOutInMunites) { public static String generateJWToken(final long userID, final String userLogin, final String isuer, final String application, final Map<String, Object> rights, final int timeOutInMunites) {
if (rsaJWK == null) { if (rsaJWK == null) {
logger.warn("JWT private key is not present !!!"); LOGGER.warn("JWT private key is not present !!!");
return null; return null;
} }
/* /*
logger.debug(" ===> expire in : " + timeOutInMunites); LOGGER.debug(" ===> expire in : " + timeOutInMunites);
logger.debug(" ===>" + new Date().getTime()); LOGGER.debug(" ===>" + new Date().getTime());
logger.debug(" ===>" + new Date(new Date().getTime())); LOGGER.debug(" ===>" + new Date(new Date().getTime()));
logger.debug(" ===>" + new Date(new Date().getTime() - 60 * timeOutInMunites * 1000)); LOGGER.debug(" ===>" + new Date(new Date().getTime() - 60 * timeOutInMunites * 1000));
*/ */
try { try {
// Create RSA-signer with the private key // Create RSA-signer with the private key
JWSSigner signer = new RSASSASigner(rsaJWK); final JWSSigner signer = new RSASSASigner(rsaJWK);
logger.warn("timeOutInMunites= {}", timeOutInMunites); LOGGER.warn("timeOutInMunites= {}", timeOutInMunites);
Date now = new Date(); final Date now = new Date();
logger.warn("now = {}", now); LOGGER.warn("now = {}", now);
Date expiration = new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */); final Date expiration = new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */);
logger.warn("expiration= {}", expiration); LOGGER.warn("expiration= {}", expiration);
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
// add right if needed: // add right if needed:
if (rights != null && !rights.isEmpty()) { if (rights != null && !rights.isEmpty()) {
builder.claim("right", rights); builder.claim("right", rights);
} }
// Prepare JWT with claims set // Prepare JWT with claims set
JWTClaimsSet claimsSet = builder.build(); final JWTClaimsSet claimsSet = builder.build();
SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet); final SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet);
// Compute the RSA signature // Compute the RSA signature
signedJWT.sign(signer); signedJWT.sign(signer);
// serialize the output... // serialize the output...
return signedJWT.serialize(); return signedJWT.serialize();
} catch (JOSEException ex) { } catch (final JOSEException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
return null; return null;
} }
public static JWTClaimsSet validateToken(String signedToken, String isuer, String application) { public static JWTClaimsSet validateToken(final String signedToken, final String isuer, final String application) {
if (rsaPublicJWK == null) {
logger.warn("JWT public key is not present !!!");
return null;
}
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
SignedJWT signedJWT = SignedJWT.parse(signedToken); final SignedJWT signedJWT = SignedJWT.parse(signedToken);
JWSVerifier verifier = new RSASSAVerifier(rsaPublicJWK); if (rsaPublicJWK == null) {
if (!signedJWT.verify(verifier)) { LOGGER.warn("JWT public key is not present !!!");
logger.error("JWT token is NOT verified "); if (!ConfigBaseVariable.getTestMode()) {
return null; return null;
} }
final String rawSignature = signedJWT.getSigningInput().toString();
if (rawSignature.equals(TestSigner.test_signature)) {
// Test token : .application..
} else {
return null;
}
} else {
final JWSVerifier verifier = new RSASSAVerifier(rsaPublicJWK);
if (!signedJWT.verify(verifier)) {
LOGGER.error("JWT token is NOT verified ");
return null;
}
}
if (!new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) { if (!new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime())) {
logger.error("JWT token is expired now = " + new Date() + " with=" + signedJWT.getJWTClaimsSet().getExpirationTime()); LOGGER.error("JWT token is expired now = " + new Date() + " with=" + signedJWT.getJWTClaimsSet().getExpirationTime());
return null; return null;
} }
if (!isuer.equals(signedJWT.getJWTClaimsSet().getIssuer())) { if (!isuer.equals(signedJWT.getJWTClaimsSet().getIssuer())) {
logger.error("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); LOGGER.error("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'");
return null; return null;
} }
if (application != null) { if (application != null) {
// TODO: verify the token is used for the correct application. // TODO: verify the token is used for the correct application.
} }
// the element must be validated outside ... // the element must be validated outside ...
//logger.debug("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'"); //LOGGER.debug("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'");
//logger.debug("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); //LOGGER.debug("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'");
return signedJWT.getJWTClaimsSet(); return signedJWT.getJWTClaimsSet();
} catch (JOSEException ex) { } catch (final JOSEException ex) {
ex.printStackTrace(); ex.printStackTrace();
} catch (ParseException e) { } catch (final ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }
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) {
if (!ConfigBaseVariable.getTestMode()) {
LOGGER.error("Test mode disable !!!!!");
return null;
}
try {
final int timeOutInMunites = 3600 * 24 * 31;
final Date now = new Date();
final Date expiration = new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */);
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
// add right if needed:
if (rights != null && !rights.isEmpty()) {
builder.claim("right", rights);
}
// Prepare JWT with claims set
final JWTClaimsSet claimsSet = builder.build();
final SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet);
// Compute the RSA signature
signedJWT.sign(new TestSigner());
// serialize the output...
return signedJWT.serialize();
} catch (final Exception ex) {
LOGGER.error("Can not generate Test Token...");
}
return null;
}
} }

View File

@ -347,4 +347,123 @@ public class TestTypes {
DataAccess.delete(TypesTable.class, insertedData.id); DataAccess.delete(TypesTable.class, insertedData.id);
} }
@Order(14)
@Test
public void testTextUpdateDirect() throws Exception {
final TypesTable test = new TypesTable();
test.textData = "test 1";
test.booleanData = null;
test.varcharData = "plop";
final TypesTable insertedData = DataAccess.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.id);
Assertions.assertTrue(insertedData.id >= 0);
// Try to retrieve all the data:
final TypesTable retrieve = DataAccess.get(TypesTable.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(insertedData.id, retrieve.id);
Assertions.assertNotNull(retrieve.textData);
Assertions.assertEquals(insertedData.textData, retrieve.textData);
Assertions.assertNull(retrieve.booleanData);
Assertions.assertNotNull(retrieve.varcharData);
Assertions.assertEquals(insertedData.varcharData, retrieve.varcharData);
// Update the text value:
retrieve.textData = "test 2";
retrieve.booleanData = true;
retrieve.varcharData = null;
final int nbUpdate = DataAccess.update(retrieve, insertedData.id);
Assertions.assertEquals(1, nbUpdate);
// Get new data
final TypesTable retrieve2 = DataAccess.get(TypesTable.class, insertedData.id);
Assertions.assertNotNull(retrieve2);
Assertions.assertNotNull(retrieve2.id);
Assertions.assertEquals(insertedData.id, retrieve2.id);
Assertions.assertNotNull(retrieve2.textData);
Assertions.assertEquals(retrieve.textData, retrieve2.textData);
Assertions.assertNotNull(retrieve2.booleanData);
Assertions.assertEquals(retrieve.booleanData, retrieve2.booleanData);
Assertions.assertNull(retrieve2.varcharData);
// test filter values:
retrieve.textData = "test 3";
retrieve.booleanData = false;
retrieve.varcharData = "test3";
final int nbUpdate2 = DataAccess.update(retrieve, insertedData.id, List.of("textData"));
Assertions.assertEquals(1, nbUpdate2);
// Get new data
final TypesTable retrieve3 = DataAccess.get(TypesTable.class, insertedData.id);
Assertions.assertNotNull(retrieve3);
Assertions.assertNotNull(retrieve3.id);
Assertions.assertEquals(insertedData.id, retrieve3.id);
Assertions.assertNotNull(retrieve3.textData);
Assertions.assertEquals(retrieve.textData, retrieve3.textData);
Assertions.assertNotNull(retrieve3.booleanData);
// note: retreive2
Assertions.assertEquals(retrieve2.booleanData, retrieve3.booleanData);
Assertions.assertNull(retrieve3.varcharData);
DataAccess.delete(TypesTable.class, insertedData.id);
}
@Order(15)
@Test
public void testTextUpdateJson() throws Exception {
final TypesTable test = new TypesTable();
test.textData = "test 1";
test.booleanData = null;
test.varcharData = "plop";
final TypesTable insertedData = DataAccess.insert(test);
Assertions.assertNotNull(insertedData);
Assertions.assertNotNull(insertedData.id);
Assertions.assertTrue(insertedData.id >= 0);
// Try to retrieve all the data:
final TypesTable retrieve = DataAccess.get(TypesTable.class, insertedData.id);
Assertions.assertNotNull(retrieve);
Assertions.assertNotNull(retrieve.id);
Assertions.assertEquals(insertedData.id, retrieve.id);
Assertions.assertNotNull(retrieve.textData);
Assertions.assertEquals(insertedData.textData, retrieve.textData);
Assertions.assertNull(retrieve.booleanData);
Assertions.assertNotNull(retrieve.varcharData);
Assertions.assertEquals(insertedData.varcharData, retrieve.varcharData);
// Update the text value:
final String jsonData = """
{
"textData": "test 2",
"booleanData": true,
"varcharData": null
}
""";
final int nbUpdate = DataAccess.updateWithJson(TypesTable.class, insertedData.id, jsonData);
Assertions.assertEquals(1, nbUpdate);
// Get new data
final TypesTable retrieve2 = DataAccess.get(TypesTable.class, insertedData.id);
Assertions.assertNotNull(retrieve2);
Assertions.assertNotNull(retrieve2.id);
Assertions.assertEquals(insertedData.id, retrieve2.id);
Assertions.assertNotNull(retrieve2.textData);
Assertions.assertEquals(retrieve.textData, retrieve2.textData);
Assertions.assertNotNull(retrieve2.booleanData);
Assertions.assertEquals(retrieve.booleanData, retrieve2.booleanData);
Assertions.assertNull(retrieve2.varcharData);
DataAccess.delete(TypesTable.class, insertedData.id);
}
} }