From d10897a086327f9b95a0326562f6ce7f3ae7e27b Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Sun, 30 Apr 2023 23:54:47 +0200 Subject: [PATCH] [DEV] security, test-unit, coverage, logger --- back/.classpath | 7 + back/.project | 4 +- back/pom.xml | 161 +++++----- back/src/org/kar/karso/WebLauncher.java | 97 ++++-- .../org/kar/karso/WebLauncherEdgeLocal.java | 3 + back/src/org/kar/karso/WebLauncherLocal.java | 15 +- .../kar/karso/api/ApplicationResource.java | 185 ++++++----- .../karso/api/ApplicationTokenResource.java | 12 +- back/src/org/kar/karso/api/Front.java | 3 + back/src/org/kar/karso/api/HealthCheck.java | 12 +- .../org/kar/karso/api/PublicKeyResource.java | 5 +- back/src/org/kar/karso/api/RightResource.java | 24 +- .../kar/karso/api/SystemConfigResource.java | 7 +- back/src/org/kar/karso/api/UserResource.java | 35 ++- .../filter/KarsoAuthenticationFilter.java | 3 + back/src/org/kar/karso/internal/Log.java | 60 ---- .../src/org/kar/karso/model/DataGetToken.java | 43 ++- back/test/resources/simplelogger.properties | 35 +++ back/test/src/test/kar/karso/TestBase.java | 289 ++++++++++++++++-- .../src/test/kar/karso/WebLauncherTest.java | 25 ++ 20 files changed, 709 insertions(+), 316 deletions(-) delete mode 100644 back/src/org/kar/karso/internal/Log.java create mode 100644 back/test/resources/simplelogger.properties create mode 100755 back/test/src/test/kar/karso/WebLauncherTest.java diff --git a/back/.classpath b/back/.classpath index 5098305..e705f7e 100644 --- a/back/.classpath +++ b/back/.classpath @@ -23,5 +23,12 @@ + + + + + + + diff --git a/back/.project b/back/.project index 25af8d9..3c43e57 100644 --- a/back/.project +++ b/back/.project @@ -32,12 +32,12 @@ - 1647191868592 + 1682721079869 30 org.eclipse.core.resources.regexFilterMatcher - node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ diff --git a/back/pom.xml b/back/pom.xml index f86dee3..272ef17 100644 --- a/back/pom.xml +++ b/back/pom.xml @@ -1,18 +1,17 @@ + - 4.0.0 - org.kar - karso - 0.4.1 - - - 3.1 17 17 - 3.1.1 @@ -21,27 +20,30 @@ https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven - - kangaroo-and-rabbit archidata 0.3.5 - - - org.junit.jupiter - junit-jupiter-api - 5.7.2 - test - + + + org.slf4j + slf4j-simple + 2.0.7 + + + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + - src test/src - ${project.basedir}/out/maven/ + ${project.basedir}/out/maven/ org.apache.maven.plugins @@ -53,14 +55,13 @@ - org.codehaus.mojo - exec-maven-plugin - 1.4.0 - - org.kar.karso.WebLauncher - + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + org.kar.karso.WebLauncher + - org.apache.maven.plugins @@ -81,41 +82,68 @@ maven-surefire-plugin 3.0.0-M5 - - maven-assembly-plugin - - - - fully.qualified.MainClass - - - - jar-with-dependencies - - - + + maven-assembly-plugin + + + + fully.qualified.MainClass + + + + jar-with-dependencies + + + - report test report + + + + jacoco-check + + check + + + + PACKAGE + + + LINE + COVEREDRATIO + 0.50 + + + + + - --> org.apache.maven.plugins @@ -126,49 +154,12 @@ true - - + + + ${basedir}/test/resources + + @@ -183,4 +174,4 @@ - + \ No newline at end of file diff --git a/back/src/org/kar/karso/WebLauncher.java b/back/src/org/kar/karso/WebLauncher.java index 956fd3a..ca3aefe 100755 --- a/back/src/org/kar/karso/WebLauncher.java +++ b/back/src/org/kar/karso/WebLauncher.java @@ -7,6 +7,7 @@ import org.kar.karso.api.ApplicationTokenResource; import org.kar.karso.api.Front; import org.kar.karso.api.HealthCheck; import org.kar.karso.api.PublicKeyResource; +import org.kar.karso.api.RightResource; import org.kar.karso.api.SystemConfigResource; import org.kar.karso.api.UserResource; import org.kar.karso.filter.KarsoAuthenticationFilter; @@ -17,9 +18,12 @@ import org.kar.karso.model.RightDescription; import org.kar.karso.model.Settings; import org.kar.karso.model.UserAuth; import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; //import org.kar.archidata.model.Migration; import org.kar.archidata.SqlWrapper; import org.kar.archidata.catcher.ExceptionCatcher; +import org.kar.archidata.catcher.FailException404API; import org.kar.archidata.catcher.FailExceptionCatcher; import org.kar.archidata.catcher.InputExceptionCatcher; import org.kar.archidata.catcher.SystemExceptionCatcher; @@ -35,44 +39,67 @@ import jakarta.ws.rs.core.UriBuilder; import java.net.URI; public class WebLauncher { - private WebLauncher() {} + final static Logger LOGGER = LoggerFactory.getLogger(WebLauncher.class); + protected ResourceConfig rc = null; + HttpServer server = null; + public WebLauncher() {} private static URI getBaseURI() { return UriBuilder.fromUri(ConfigBaseVariable.getlocalAddress()).build(); } public static void main(String[] args) throws InterruptedException { - ConfigBaseVariable.bdDatabase = - "karso"; - - try { - JWTWrapper.initLocalToken(ConfigVariable.getUUIDKeyRoot()); - } catch (Exception e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - System.out.println("Wait 10 seconds ...."); - Thread.sleep(10000); - return; - } - + WebLauncher launcher = new WebLauncher(); + launcher.process(); + WebLauncher.LOGGER.info("end-configure the server & wait finish process:"); + Thread.currentThread().join(); + WebLauncher.LOGGER.info("STOP the REST server:"); + } + + public void generateDB() { + // generate the BDD: try { String out = ""; out += SqlWrapper.createTable(Settings.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += SqlWrapper.createTable(UserAuth.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += SqlWrapper.createTable(Application.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += SqlWrapper.createTable(ApplicationToken.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += SqlWrapper.createTable(RightDescription.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += SqlWrapper.createTable(Right.class); + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; // default admin: "karadmin" password: "adminA@666" out += """ INSERT INTO `application` (`id`, `name`, `description`, `redirect`, `redirectDev`, `notification`, `ttl`) VALUES ('0', 'karso', 'Root SSO interface', 'http://atria-soft/karso', '', '', '666'); """; + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += """ INSERT INTO `user` (`id`, `login`, `password`, `email`, `admin`) VALUES ('0', 'karadmin', '0ddcac5ede3f1300a1ce5948ab15112f2810130531d578ab8bc4dc131652d7cf7a3ff6e827eb957bff43bc2c65a6a1d46722e5b3a2343ac3176a33ea7250080b', 'admin@admin.ZZZ', 1); """; + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += """ INSERT INTO `settings` (`key`, `right`, `type`, `value`) VALUES ('SIGN_UP_ENABLE', 'rwr-r-', 'BOOLEAN', 'false'), @@ -80,30 +107,55 @@ public class WebLauncher { ('SIGN_UP_FILTER', 'rw----', 'STRING', '.*'), ('EMAIL_VALIDATION_REQUIRED', 'rwr-r-', 'BOOLEAN', 'false'); """; + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += """ INSERT INTO `rightDescription` (`id`, `applicationId`, `key`, `title`, `description`, `type`) VALUES (0, 0, 'ADMIN', 'Administrator', 'Full administrator Right', 'BOOLEAN'); """; + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; out += """ INSERT INTO `right` (`applicationId`, `userId`, `rightDescriptionId`, `value`) VALUES (0, 0, 0, 'true'); """; + LOGGER.debug(out); + SqlWrapper.executeSimpleQuerry(out); + out = ""; //out += SqlWrapper.createTable(Migration.class); - System.out.println(out); + LOGGER.debug(out); // Do initialization or migration: - //SqlWrapper.executeSimpleQuerry(out); + SqlWrapper.executeSimpleQuerry(out); } catch (Exception e) { // TODO Auto-generated catch block + LOGGER.error("can not generate the BD: {}", e.getMessage()); e.printStackTrace(); } + } + + public void process() throws InterruptedException { + ConfigBaseVariable.bdDatabase = "karso"; + + try { + JWTWrapper.initLocalToken(ConfigVariable.getUUIDKeyRoot()); + } catch (Exception e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + LOGGER.info("Wait 10 seconds ...."); + Thread.sleep(10000); + return; + } + // =================================================================== // Configure resources // =================================================================== - ResourceConfig rc = new ResourceConfig(); + rc = new ResourceConfig(); // global authentication system rc.register(new OptionFilter()); // remove cors ==> all time called by an other system... @@ -113,6 +165,7 @@ public class WebLauncher { rc.register(InputExceptionCatcher.class); rc.register(SystemExceptionCatcher.class); rc.register(FailExceptionCatcher.class); + rc.register(FailException404API.class); rc.register(ExceptionCatcher.class); // add default resource: @@ -121,6 +174,7 @@ public class WebLauncher { rc.registerClasses(ApplicationResource.class); rc.registerClasses(ApplicationTokenResource.class); rc.registerClasses(SystemConfigResource.class); + rc.registerClasses(RightResource.class); rc.registerClasses(Front.class); rc.registerClasses(HealthCheck.class); // add jackson to be discover when we are ins stand-alone server @@ -128,11 +182,11 @@ public class WebLauncher { // enable this to show low level request //rc.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, Level.WARNING.getName()); - HttpServer server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc); + server = GrizzlyHttpServerFactory.createHttpServer(getBaseURI(), rc); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { - System.out.println("Stopping server.."); + LOGGER.info("Stopping server.."); server.shutdownNow(); } }, "shutdownHook")); @@ -142,10 +196,9 @@ public class WebLauncher { // =================================================================== try { server.start(); - System.out.println("Jersey app started at " + getBaseURI()); - Thread.currentThread().join(); + LOGGER.debug("Jersey app started at {}", getBaseURI()); } catch (Exception e) { - System.out.println("There was an error while starting Grizzly HTTP server."); + LOGGER.error("There was an error while starting Grizzly HTTP server."); e.printStackTrace(); } } diff --git a/back/src/org/kar/karso/WebLauncherEdgeLocal.java b/back/src/org/kar/karso/WebLauncherEdgeLocal.java index 298d37b..de3cab4 100755 --- a/back/src/org/kar/karso/WebLauncherEdgeLocal.java +++ b/back/src/org/kar/karso/WebLauncherEdgeLocal.java @@ -3,8 +3,11 @@ package org.kar.karso; import org.kar.archidata.util.ConfigBaseVariable; import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class WebLauncherEdgeLocal { + final Logger logger = LoggerFactory.getLogger(WebLauncherEdgeLocal.class); private WebLauncherEdgeLocal() {} public static void main(String[] args) throws InterruptedException { diff --git a/back/src/org/kar/karso/WebLauncherLocal.java b/back/src/org/kar/karso/WebLauncherLocal.java index 9f7bced..6e3f473 100755 --- a/back/src/org/kar/karso/WebLauncherLocal.java +++ b/back/src/org/kar/karso/WebLauncherLocal.java @@ -3,11 +3,22 @@ package org.kar.karso; import org.kar.archidata.util.ConfigBaseVariable; import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class WebLauncherLocal { +public class WebLauncherLocal extends WebLauncher { + final Logger logger = LoggerFactory.getLogger(WebLauncherLocal.class); private WebLauncherLocal() {} public static void main(String[] args) throws InterruptedException { + WebLauncherLocal launcher = new WebLauncherLocal(); + launcher.process(); + launcher.logger.info("end-configure the server & wait finish process:"); + Thread.currentThread().join(); + launcher.logger.info("STOP the REST server:"); + } + @Override + public void process() throws InterruptedException { if (true) { // for local test: ConfigBaseVariable.apiAdress = "http://0.0.0.0:15080/karso/api/"; @@ -18,6 +29,6 @@ public class WebLauncherLocal { //ConfigBaseVariable.dbHost = "./bdd_base.sqlite"; } - WebLauncher.main(args); + super.process(); } } diff --git a/back/src/org/kar/karso/api/ApplicationResource.java b/back/src/org/kar/karso/api/ApplicationResource.java index 77bc121..50dd525 100755 --- a/back/src/org/kar/karso/api/ApplicationResource.java +++ b/back/src/org/kar/karso/api/ApplicationResource.java @@ -4,6 +4,8 @@ import org.kar.archidata.SqlWrapper; import org.kar.archidata.WhereCondition; import org.kar.archidata.filter.GenericContext; import org.kar.karso.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.kar.archidata.util.JWTWrapper; import org.kar.archidata.annotation.security.RolesAllowed; import org.kar.archidata.exception.InputException; @@ -23,6 +25,7 @@ import jakarta.ws.rs.core.SecurityContext; @Path("/application") @Produces( MediaType.APPLICATION_JSON) public class ApplicationResource { + final Logger logger = LoggerFactory.getLogger(ApplicationResource.class); public ApplicationResource() { } @@ -39,7 +42,7 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return out; } for (UserLinkApplication app : links) { @@ -60,31 +63,26 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return out; } - System.out.println("Find list of user for an application :" + links); + logger.debug("Find list of user for an application: {}", links); for (UserLinkApplication app : links) { out.add(app.user_id); } return out; } - - @GET - @Path("{id}/users") - @RolesAllowed(value= {"ADMIN"}) - public List getApplicationUsers(@PathParam("id") Long applicationId) throws Exception { - // special case for SSO: (all user have access on the SSO...). - - System.out.println("Request list of user for an applciation:" + applicationId); - return getListOfUsers(applicationId); - } - + //////////////////////////////////////////////////////////////////////////////////////// + // + // Generic /application/ + // + //////////////////////////////////////////////////////////////////////////////////////// + @GET @RolesAllowed(value= {"USER", "ADMIN"}) public List getApplications(@Context SecurityContext sc) throws Exception { GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("getApplications"); + logger.debug("getApplications"); // TODO filter with the list of element available in his authorizations ... List tmp = SqlWrapper.gets(Application.class, false); if (gc.userByToken.hasRight("ADMIN", true)) { @@ -99,13 +97,86 @@ public class ApplicationResource { } return out; } + + @POST + @RolesAllowed("ADMIN") + public Application create(Application application) throws Exception { + logger.debug("create new application {}", application); + // verify login or email is correct: + if (application.name == null || application.name.length() < 5) { + throw new InputException("name", "create application (name too small: '" + application.name + "')"); + } + if (application.redirect == null || application.redirect.length() < 6) { + throw new InputException("redirect", "create application (redirect too small: '" + application.redirect + "')"); + } + application.id = null; + application.create_date = null; + application.deleted = null; + application.modify_date = null; + return SqlWrapper.insert(application); + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Generic /application/{id} + // + //////////////////////////////////////////////////////////////////////////////////////// + + @GET + @Path("{id}") + @RolesAllowed("ADMIN") + public Application get(@PathParam("id") Long id) throws Exception { + return SqlWrapper.get(Application.class, id); + } + + @PUT + @Path("{id}") + @RolesAllowed("ADMIN") + @Consumes(MediaType.APPLICATION_JSON) + public Application put(@PathParam("id") Long id, String jsonRequest) throws Exception { + SqlWrapper.update(Application.class, id, jsonRequest); + return SqlWrapper.get(Application.class, id); + } + + @DELETE + @Path("{id}") + @RolesAllowed("ADMIN") + @Produces( value = MediaType.TEXT_PLAIN ) + public void remove(@Context SecurityContext sc, @PathParam("id") long applicationId) throws Exception { + SqlWrapper.setDelete(Application.class, applicationId); + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Generic /{id}/* + // + //////////////////////////////////////////////////////////////////////////////////////// + + + @GET + @Path("{id}/users") + @RolesAllowed(value= {"ADMIN"}) + public List getApplicationUsers(@PathParam("id") Long applicationId) throws Exception { + // special case for SSO: (all user have access on the SSO...). + + logger.debug("Request list of user for an applciation: {}", applicationId); + return getListOfUsers(applicationId); + } + + //////////////////////////////////////////////////////////////////////////////////////// + // + // Generic /application/* + // + //////////////////////////////////////////////////////////////////////////////////////// + @GET @Path("small") @RolesAllowed(value= {"USER", "ADMIN"}) public List getApplicationsSmall(@Context SecurityContext sc) throws Exception { GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("getApplications"); + logger.debug("getApplications"); List tmp = SqlWrapper.gets(Application.class, false); List regular = this.getUserListOfApplication(gc.userByToken.id); List out = new ArrayList<>(); @@ -122,11 +193,11 @@ public class ApplicationResource { @RolesAllowed(value= {"USER", "ADMIN"}) public Response getClientToken(@Context SecurityContext sc, @QueryParam("application") String application) throws Exception { GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("== USER ? " + gc.userByToken); + logger.debug("== USER ? {}", gc.userByToken); if (application == null) { String result = "Input error missing parameter: 'application'"; - System.out.println(" result: " + result); + logger.debug(" result: {}", result); return Response.status(406).entity(result).build(); } String applicationName = application; @@ -135,7 +206,7 @@ public class ApplicationResource { applicationName = applicationName.substring(0, applicationName.length()-4); isDev = true; } - System.out.println("Search for '" + applicationName + "' base of '" + application + "'"); + logger.debug("Search for '{}' base of '{}'", applicationName, application); Application appl = null; try { @@ -144,17 +215,17 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.debug(" result: {}", result); return Response.status(500).entity(result).build(); } if (appl == null) { String result = "Authentiocate-wrong email/login '" + applicationName + "')"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(401).entity(result).build(); } if (false) { - // the pplication id is not streamed in the application liny in the where elements + // the application id is not streamed in the application liny in the where elements // get the local user: UserAuth localUser = null; try { @@ -163,18 +234,18 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(500).entity(result).build(); } if (localUser == null) { String result = "Authenticate-wrong results '" + applicationName + "')"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(401).entity(result).build(); } - System.out.println("Get application list: " + localUser.applications); + logger.debug("Get application list: " + localUser.applications); if (localUser.applications == null || localUser.applications.indexOf((Long)gc.userByToken.id) == -1) { String result = "Authenticate impossible ==> application not accessible '" + applicationName + "'"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(401).entity(result).build(); } } else { @@ -190,12 +261,12 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(500).entity(result).build(); } if (links == null) { String result = "Authenticate impossible ==> application not accessible '" + applicationName + "'"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(401).entity(result).build(); } } @@ -209,7 +280,7 @@ public class ApplicationResource { // we set the right in the under map to manage multiple application group right. and in some application user can see other user or all user of the application outRight.put(applicationName, applicationRight); String ret = JWTWrapper.generateJWToken(gc.userByToken.id, gc.userByToken.name, "KarAuth", applicationName, outRight, appl.ttl); - //System.out.println(" ==> generate token: " + ret); + //logger.debug(" ==> generate token: {}", ret); String returnAdress = appl.redirect; if (isDev) { returnAdress = appl.redirectDev; @@ -221,15 +292,15 @@ public class ApplicationResource { @Path("return") @RolesAllowed(value= {"USER", "ADMIN"}) public Response logOut(@Context SecurityContext sc, @QueryParam("application") String application) { - System.out.println("====================================="); - System.out.println("Get log_out()"); - System.out.println("====================================="); + logger.debug("====================================="); + logger.debug("Get log_out()"); + logger.debug("====================================="); GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("== USER ? " + gc.userByToken); + logger.debug("== USER ? {}", gc.userByToken); if (application == null) { String result = "Input error missing parameter: 'application'"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(406).entity(result).build(); } String applicationName = application; @@ -238,7 +309,7 @@ public class ApplicationResource { applicationName = applicationName.substring(0, applicationName.length()-4); isDev = true; } - System.out.println("Search for '" + applicationName + "' base of '" + application + "'"); + logger.debug("Search for '{}' base of '{}'", applicationName, application); Application appl = null; try { @@ -247,13 +318,13 @@ public class ApplicationResource { // TODO Auto-generated catch block e.printStackTrace(); String result = "SERVER Internal error"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(500).entity(result).build(); } if (appl == null) { String result = "Authentiocate-wrong email/login '" + applicationName + "')"; - System.out.println(" result: " + result); + logger.error(" result: {}", result); return Response.status(404).entity(result).build(); } String returnAdress = appl.redirect; @@ -263,46 +334,6 @@ public class ApplicationResource { return Response.status(201).entity("{ \"url\":\"" + returnAdress + "\"}").build(); } - @DELETE - @Path("{id}") - @RolesAllowed("ADMIN") - @Produces( value = MediaType.TEXT_PLAIN ) - public void remove(@Context SecurityContext sc, @PathParam("id") long applicationId) throws Exception { - SqlWrapper.setDelete(Application.class, applicationId); - } - - @POST - @RolesAllowed("ADMIN") - public Application create(Application application) throws Exception { - System.out.println("create new application " + application); - // verify login or email is correct: - if (application.name == null || application.name.length() < 5) { - throw new InputException("name", "create application (name too small: '" + application.name + "')"); - } - if (application.redirect == null || application.redirect.length() < 6) { - throw new InputException("redirect", "create application (redirect too small: '" + application.redirect + "')"); - } - application.id = null; - application.create_date = null; - application.deleted = null; - application.modify_date = null; - return SqlWrapper.insert(application); - } - - @PUT - @Path("{id}") - @RolesAllowed("ADMIN") - @Consumes(MediaType.APPLICATION_JSON) - public Application put(@PathParam("id") Long id, String jsonRequest) throws Exception { - SqlWrapper.update(Application.class, id, jsonRequest); - return SqlWrapper.get(Application.class, id); - } - @GET - @Path("{id}") - @RolesAllowed("ADMIN") - public Application put(@PathParam("id") Long id) throws Exception { - return SqlWrapper.get(Application.class, id); - } } diff --git a/back/src/org/kar/karso/api/ApplicationTokenResource.java b/back/src/org/kar/karso/api/ApplicationTokenResource.java index c2256e2..a7c3d85 100755 --- a/back/src/org/kar/karso/api/ApplicationTokenResource.java +++ b/back/src/org/kar/karso/api/ApplicationTokenResource.java @@ -1,9 +1,10 @@ package org.kar.karso.api; -import org.glassfish.jersey.media.multipart.FormDataParam; import org.kar.archidata.SqlWrapper; import org.kar.archidata.WhereCondition; import org.kar.karso.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.kar.archidata.annotation.security.RolesAllowed; import org.kar.archidata.exception.InputException; @@ -23,10 +24,17 @@ import jakarta.ws.rs.core.SecurityContext; @Path("/application_token") @Produces( MediaType.APPLICATION_JSON) public class ApplicationTokenResource { + final Logger logger = LoggerFactory.getLogger(ApplicationTokenResource.class); public ApplicationTokenResource() { } + //////////////////////////////////////////////////////////////////////////////////////// + // + // Generic /application_token/{applicationId} + // + //////////////////////////////////////////////////////////////////////////////////////// + @GET @Path("{applicationId}") @RolesAllowed(value= {"ADMIN"}) @@ -103,7 +111,7 @@ public class ApplicationTokenResource { // correct input string stream : String name = multipartCorrection(request.name()); //validity = multipartCorrection(validity); - System.out.println("create a nexw token..."); + logger.debug("create a nexw token..."); if (applicationId == null) { throw new InputException("applicationId", "can not be null"); } diff --git a/back/src/org/kar/karso/api/Front.java b/back/src/org/kar/karso/api/Front.java index 1a1c372..084fea7 100644 --- a/back/src/org/kar/karso/api/Front.java +++ b/back/src/org/kar/karso/api/Front.java @@ -4,9 +4,12 @@ import jakarta.ws.rs.*; import org.kar.archidata.api.FrontGeneric; import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @Path("/front") public class Front extends FrontGeneric { + final Logger logger = LoggerFactory.getLogger(FrontGeneric.class); public Front() { this.baseFrontFolder = ConfigVariable.getFrontFolder(); } diff --git a/back/src/org/kar/karso/api/HealthCheck.java b/back/src/org/kar/karso/api/HealthCheck.java index 1f919ac..3702b71 100644 --- a/back/src/org/kar/karso/api/HealthCheck.java +++ b/back/src/org/kar/karso/api/HealthCheck.java @@ -1,19 +1,17 @@ package org.kar.karso.api; import org.kar.archidata.annotation.security.PermitAll; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @Path("/health_check") @Produces(MediaType.APPLICATION_JSON) public class HealthCheck { - public class HealthResult { - public String value; - public HealthResult(String value) { - this.value = value; - } - } - // todo : do it better... + final static Logger LOGGER = LoggerFactory.getLogger(HealthCheck.class); + public record HealthResult(String value) {}; @GET @PermitAll public HealthResult getHealth() { diff --git a/back/src/org/kar/karso/api/PublicKeyResource.java b/back/src/org/kar/karso/api/PublicKeyResource.java index 9915b90..fb306a5 100755 --- a/back/src/org/kar/karso/api/PublicKeyResource.java +++ b/back/src/org/kar/karso/api/PublicKeyResource.java @@ -2,10 +2,11 @@ package org.kar.karso.api; import org.kar.archidata.util.JWTWrapper; import org.kar.archidata.util.JWTWrapper.PublicKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.nimbusds.jose.JOSEException; -import org.kar.archidata.annotation.security.PermitAll; import org.kar.archidata.annotation.security.RolesAllowed; import java.security.interfaces.RSAPublicKey; @@ -17,6 +18,7 @@ import jakarta.ws.rs.core.MediaType; @Path("/public_key") @Produces(MediaType.APPLICATION_JSON) public class PublicKeyResource { + final Logger logger = LoggerFactory.getLogger(PublicKeyResource.class); public PublicKeyResource() { @@ -24,7 +26,6 @@ public class PublicKeyResource { // This is for java server that use the same implementation // curl http://localhost:9993/public_key @GET - @PermitAll @RolesAllowed(value= {"APPLICATION"}) public PublicKey getKey() { return new PublicKey(JWTWrapper.getPublicKeyJson()); diff --git a/back/src/org/kar/karso/api/RightResource.java b/back/src/org/kar/karso/api/RightResource.java index 25b44b0..33f6574 100644 --- a/back/src/org/kar/karso/api/RightResource.java +++ b/back/src/org/kar/karso/api/RightResource.java @@ -4,6 +4,8 @@ import org.kar.archidata.SqlWrapper; import org.kar.archidata.WhereCondition; import org.kar.karso.model.Right; import org.kar.karso.model.RightDescription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.kar.archidata.annotation.security.RolesAllowed; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @@ -16,6 +18,7 @@ import java.util.Map; @Path("/right") @Produces({MediaType.APPLICATION_JSON}) public class RightResource { + final static Logger logger = LoggerFactory.getLogger(RightResource.class); public static Object transform(String type, String value) { @@ -39,7 +42,7 @@ public class RightResource { new WhereCondition("applicationId", "=", applicationId), new WhereCondition("deleted", "=", 0) )); - System.out.println("Get some descriptions: " + rightsDescriptions.size() + " applicationId=" + applicationId); + logger.debug("Get some descriptions: {} applicationId={}", rightsDescriptions.size(), applicationId); if (rightsDescriptions != null && rightsDescriptions.size() != 0) { List rights = SqlWrapper.getsWhere(Right.class, List.of( @@ -47,7 +50,7 @@ public class RightResource { new WhereCondition("userId", "=", userId), new WhereCondition("deleted", "=", 0) )); - System.out.println("Get some user right: " + rights.size() + " userID=" + userId); + logger.debug("Get some user right: {}userID={}", rights.size(), userId); if (rights != null && rights.size() != 0) { for (Right right: rights) { RightDescription description = rightsDescriptions.stream() @@ -59,21 +62,15 @@ public class RightResource { } } } else { - System.out.println("The User have no specific right..."); + logger.debug("The User have no specific right..."); } } else { // the application does not manage right with Karso (normal use-case) - System.out.println("Does not manage Karso right..."); + logger.debug("Does not manage Karso right..."); } return out; } - @GET - @Path("{id}") - @RolesAllowed("ADMIN") - public static Right getWithId(@PathParam("id") Long id) throws Exception { - return SqlWrapper.get(Right.class, id); - } @GET @RolesAllowed("ADMIN") @@ -87,6 +84,13 @@ public class RightResource { public Right post(String jsonRequest) throws Exception { return SqlWrapper.insertWithJson(Right.class, jsonRequest); } + + @GET + @Path("{id}") + @RolesAllowed("ADMIN") + public static Right getWithId(@PathParam("id") Long id) throws Exception { + return SqlWrapper.get(Right.class, id); + } @PUT @Path("{id}") diff --git a/back/src/org/kar/karso/api/SystemConfigResource.java b/back/src/org/kar/karso/api/SystemConfigResource.java index 17fb3bd..a9f06ce 100755 --- a/back/src/org/kar/karso/api/SystemConfigResource.java +++ b/back/src/org/kar/karso/api/SystemConfigResource.java @@ -2,6 +2,8 @@ package org.kar.karso.api; import org.kar.archidata.SqlWrapper; import org.kar.karso.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -22,6 +24,7 @@ import jakarta.ws.rs.core.SecurityContext; @Path("/system_config") @Produces( MediaType.APPLICATION_JSON) public class SystemConfigResource { + final Logger logger = LoggerFactory.getLogger(SystemConfigResource.class); public static class GetSignUpAvaillable { public boolean signup; @@ -48,7 +51,7 @@ public class SystemConfigResource { } boolean availlable = "true".equalsIgnoreCase(set.value); GetSignUpAvaillable tmp = new GetSignUpAvaillable(availlable); - System.out.println("mlkmlk " + tmp.signup); + logger.debug("mlkmlk {}", tmp.signup); return tmp; } @@ -90,7 +93,7 @@ public class SystemConfigResource { JsonNode value = root.findPath("value"); res.value = value.asText(); - System.out.println("Update value : " + res.value); + logger.debug("Update value : {}", res.value); SqlWrapper.update(res, res.id, List.of("value")); return Response.status(201).entity("{ \"value\":\"" + res.value + "\"}").build(); } diff --git a/back/src/org/kar/karso/api/UserResource.java b/back/src/org/kar/karso/api/UserResource.java index 9d91a80..44c6d08 100755 --- a/back/src/org/kar/karso/api/UserResource.java +++ b/back/src/org/kar/karso/api/UserResource.java @@ -11,6 +11,8 @@ import org.kar.archidata.exception.SystemException; import org.kar.archidata.filter.GenericContext; import org.kar.karso.model.*; import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonInclude; @@ -34,6 +36,7 @@ import java.time.LocalDateTime; @Path("/users") @Produces( MediaType.APPLICATION_JSON) public class UserResource { + final Logger logger = LoggerFactory.getLogger(UserResource.class); @JsonInclude(JsonInclude.Include.NON_NULL) public class UserOut { @@ -59,7 +62,7 @@ public class UserResource { @Path("{id}") @RolesAllowed("ADMIN") public UserAuthGet getUser(@Context SecurityContext sc, @PathParam("id") long userId) throws Exception { - GenericContext gc = (GenericContext) sc.getUserPrincipal(); + //GenericContext gc = (GenericContext) sc.getUserPrincipal(); return SqlWrapper.get(UserAuthGet.class, userId); } @@ -70,7 +73,7 @@ public class UserResource { @PathParam("userId") long userId, @PathParam("applicationId") long applicationId, boolean data) throws Exception { - System.out.println("Find typeNode"); + logger.debug("Find typeNode"); if (data == true) { SqlWrapper.addLink(UserAuth.class, userId, "application", applicationId); } else { @@ -110,7 +113,7 @@ public class UserResource { @Path("create_new_user") @RolesAllowed("ADMIN") public UserAuthGet createUser(UserCreate user) throws Exception { - System.out.println("create new User email=" + user.email + " login=" + user.login); + logger.debug("create new User email={} login={}", user.email, user.login); // verify login or email is correct: if (user.login == null || user.login.length() < 6) { throw new InputException("login", "Authentiocate-method-error (login too small: '" + user.login + "')"); @@ -154,7 +157,7 @@ public class UserResource { newUser.email = user.email; newUser.lastConnection = Timestamp.valueOf(LocalDateTime.now()); UserAuth tmp = SqlWrapper.insert(newUser); - System.out.println("create new user done with id==" + tmp.id); + logger.debug("create new user done with id=={}", tmp.id); return SqlWrapper.get(UserAuthGet.class, tmp.id); } @@ -162,9 +165,9 @@ public class UserResource { @Path("me") @RolesAllowed("USER") public UserOut getMe(@Context SecurityContext sc) { - System.out.println("getMe()"); + logger.debug("getMe()"); GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("== USER ? " + gc.userByToken); + logger.debug("== USER ? {}", gc.userByToken); return new UserOut(gc.userByToken.id, gc.userByToken.name); } @@ -172,9 +175,9 @@ public class UserResource { @Path("password") @RolesAllowed("USER") public Response changePassword(@Context SecurityContext sc, ChangePassword data) throws Exception { - System.out.println("ChangePassword()"); + logger.debug("ChangePassword()"); GenericContext gc = (GenericContext) sc.getUserPrincipal(); - System.out.println("== USER ? " + gc.userByToken); + logger.debug("== USER ? {}", gc.userByToken); if(data == null) { throw new InputException("data", "No data set..."); @@ -213,14 +216,14 @@ public class UserResource { @Path("/check_login") @PermitAll public Response checkLogin(@QueryParam("login") String login) throws Exception { - System.out.println("checkLogin: " + login ); + logger.debug("checkLogin: '{}'", login ); List out = SqlWrapper.getsWhere(UserAuth.class, List.of( new WhereCondition("login", "=", login) ), false); if (out.size() >= 1) { return Response.ok().build(); } - return Response.status(404).build(); + throw new NotFoundException("User does not exist: '" + login + "'"); } // TODO: add an application TOKEN and permit only 50 requested (maybe add an option to disable it). @@ -228,14 +231,14 @@ public class UserResource { @Path("/check_email") @PermitAll public Response checkEmail(@QueryParam("email") String email) throws Exception { - System.out.println("checkEmail: " + email ); + logger.debug("checkEmail: {}", email ); List out = SqlWrapper.getsWhere(UserAuth.class, List.of( new WhereCondition("email", "=", email) ), false); if (out.size() >= 1) { return Response.ok().build(); } - return Response.status(404).build(); + throw new NotFoundException("emain does not exist: '" + email + "'"); } @@ -270,7 +273,7 @@ public class UserResource { if (!passwodCheck.contentEquals(password)) { throw new FailException(Response.Status.PRECONDITION_FAILED , "Password error ..."); } - System.out.println(" ==> pass nearly all test : admin=" + user.admin + " blocked=" + user.blocked + " removed=" + user.removed); + logger.debug(" ==> pass nearly all test : admin={} blocked={} removed={}", user.admin, user.blocked, user.removed); if (user.blocked || user.removed) { throw new FailException(Response.Status.UNAUTHORIZED, "FAIL Authentiocate"); } @@ -282,7 +285,7 @@ public class UserResource { @PermitAll @Consumes(MediaType.APPLICATION_JSON) public GetToken getToken(DataGetToken data) throws Exception { - UserAuth user = checkAuthUser(data.method, data.login, data.time, data.password); + UserAuth user = checkAuthUser(data.method(), data.login(), data.time(), data.password()); // at the point the user has been not deleted and not blocked. // this authentication is valid only for Karso ==> not for the application int expirationTimeInMinutes = ConfigVariable.getAuthExpirationTime(); @@ -293,7 +296,7 @@ public class UserResource { // If the USER is not override, the system add by default USER ssoRight.put("USER", true); } - System.out.println("Get new token with right: " + ssoRight); + logger.debug("Get new token with right: {}", ssoRight); Map outRight = new HashMap<>(); String applicationName = "karso"; // we set the right in the under map to manage multiple application group right. and in some application user can see other user or all user of the application @@ -305,7 +308,7 @@ public class UserResource { newUser.lastConnection = Timestamp.valueOf(LocalDateTime.now()); SqlWrapper.update(newUser, user.id, List.of("lastConnection")); - //System.out.println(" ==> generate token: " + ret); + //logger.debug(" ==> generate token: {}", ret); return new GetToken(ret); } diff --git a/back/src/org/kar/karso/filter/KarsoAuthenticationFilter.java b/back/src/org/kar/karso/filter/KarsoAuthenticationFilter.java index ec038d2..1f00f39 100644 --- a/back/src/org/kar/karso/filter/KarsoAuthenticationFilter.java +++ b/back/src/org/kar/karso/filter/KarsoAuthenticationFilter.java @@ -11,6 +11,8 @@ import jakarta.ws.rs.ext.Provider; import org.kar.archidata.SqlWrapper; import org.kar.archidata.model.UserByToken; import org.kar.karso.model.ApplicationToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import jakarta.annotation.Priority; @@ -18,6 +20,7 @@ import jakarta.annotation.Priority; @Provider @Priority(Priorities.AUTHENTICATION) public class KarsoAuthenticationFilter extends AuthenticationFilter { + final Logger logger = LoggerFactory.getLogger(KarsoAuthenticationFilter.class); //curl http://0.0.0.0:15080/karso/api/public_key/pem --output plop.txt -H "Authorization: Zota 1:U0sJM1m@-STSdfg4365fJOFUGbR4kFycBu1qGZPwf7gW6k2WWRBzTPUH7QutCgPw-SDss45_563sSDFdfg@dsf@456" --verbose diff --git a/back/src/org/kar/karso/internal/Log.java b/back/src/org/kar/karso/internal/Log.java deleted file mode 100644 index ab41540..0000000 --- a/back/src/org/kar/karso/internal/Log.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.kar.karso.internal; - -//import io.scenarium.logger.LogLevel; -//import io.scenarium.logger.Logger; - -public class Log { -// private static final String LIB_NAME = "logger"; -// private static final String LIB_NAME_DRAW = Logger.getDrawableName(LIB_NAME); -// private static final boolean PRINT_CRITICAL = Logger.getNeedPrint(LIB_NAME, LogLevel.CRITICAL); -// private static final boolean PRINT_ERROR = Logger.getNeedPrint(LIB_NAME, LogLevel.ERROR); -// private static final boolean PRINT_WARNING = Logger.getNeedPrint(LIB_NAME, LogLevel.WARNING); -// private static final boolean PRINT_INFO = Logger.getNeedPrint(LIB_NAME, LogLevel.INFO); -// private static final boolean PRINT_DEBUG = Logger.getNeedPrint(LIB_NAME, LogLevel.DEBUG); -// private static final boolean PRINT_VERBOSE = Logger.getNeedPrint(LIB_NAME, LogLevel.VERBOSE); -// private static final boolean PRINT_TODO = Logger.getNeedPrint(LIB_NAME, LogLevel.TODO); -// private static final boolean PRINT_PRINT = Logger.getNeedPrint(LIB_NAME, LogLevel.PRINT); -// -// private Log() {} -// -// public static void print(String data) { -// if (PRINT_PRINT) -// Logger.print(LIB_NAME_DRAW, data); -// } -// -// public static void todo(String data) { -// if (PRINT_TODO) -// Logger.todo(LIB_NAME_DRAW, data); -// } -// -// public static void critical(String data) { -// if (PRINT_CRITICAL) -// Logger.critical(LIB_NAME_DRAW, data); -// } -// -// public static void error(String data) { -// if (PRINT_ERROR) -// Logger.error(LIB_NAME_DRAW, data); -// } -// -// public static void warning(String data) { -// if (PRINT_WARNING) -// Logger.warning(LIB_NAME_DRAW, data); -// } -// -// public static void info(String data) { -// if (PRINT_INFO) -// Logger.info(LIB_NAME_DRAW, data); -// } -// -// public static void debug(String data) { -// if (PRINT_DEBUG) -// Logger.debug(LIB_NAME_DRAW, data); -// } -// -// public static void verbose(String data) { -// if (PRINT_VERBOSE) -// Logger.verbose(LIB_NAME_DRAW, data); -// } - -} diff --git a/back/src/org/kar/karso/model/DataGetToken.java b/back/src/org/kar/karso/model/DataGetToken.java index db80665..a80f3fb 100644 --- a/back/src/org/kar/karso/model/DataGetToken.java +++ b/back/src/org/kar/karso/model/DataGetToken.java @@ -1,8 +1,41 @@ package org.kar.karso.model; -public class DataGetToken { - public String login; - public String method; - public String time; - public String password; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public record DataGetToken(String login, + String method, + String time, + String password + ) { + + /*public DataGetToken(String login, String method, + String time, + String password) { + this(login, method, time, password); + }*/ + + public static String sha512(String passwordToHash) { //, String salt){ + String generatedPassword = null; + try { + MessageDigest md = MessageDigest.getInstance("SHA-512"); + //md.update(salt.getBytes(StandardCharsets.UTF_8)); + byte[] bytes = md.digest(passwordToHash.getBytes(StandardCharsets.UTF_8)); + StringBuilder sb = new StringBuilder(); + for(int i=0; i< bytes.length ;i++){ + sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1)); + } + generatedPassword = sb.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return generatedPassword; + } + public static DataGetToken generate(String login, String method, String time, String password) { + return generateSha(login, method , time , sha512(password)); + } + public static DataGetToken generateSha(String login, String method, String time, String password) { + return new DataGetToken(login, method , time , sha512("login='" + login + "';pass='" + password + "';date='" + time + "'")); + } } diff --git a/back/test/resources/simplelogger.properties b/back/test/resources/simplelogger.properties new file mode 100644 index 0000000..4314b58 --- /dev/null +++ b/back/test/resources/simplelogger.properties @@ -0,0 +1,35 @@ +# SLF4J's SimpleLogger configuration file +# Simple implementation of Logger that sends all enabled log messages, for all defined loggers, to System.err. +# Default logging detail level for all instances of SimpleLogger. +# Must be one of ("trace", "debug", "info", "warn", or "error"). +# If not specified, defaults to "info". +org.slf4j.simpleLogger.defaultLogLevel=trace + +# Logging detail level for a SimpleLogger instance named "xxxxx". +# Must be one of ("trace", "debug", "info", "warn", or "error"). +# If not specified, the default logging detail level is used. +#org.slf4j.simpleLogger.log.xxxxx= + +# Set to true if you want the current date and time to be included in output messages. +# Default is false, and will output the number of milliseconds elapsed since startup. +#org.slf4j.simpleLogger.showDateTime=false + +# The date and time format to be used in the output messages. +# The pattern describing the date and time format is the same that is used in java.text.SimpleDateFormat. +# If the format is not specified or is invalid, the default format is used. +# The default format is yyyy-MM-dd HH:mm:ss:SSS Z. +#org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS Z + +# Set to true if you want to output the current thread name. +# Defaults to true. +org.slf4j.simpleLogger.showThreadName=true + +# Set to true if you want the Logger instance name to be included in output messages. +# Defaults to true. +#org.slf4j.simpleLogger.showLogName=true + +# Set to true if you want the last component of the name to be included in output messages. +# Defaults to false. +#org.slf4j.simpleLogger.showShortLogName=false + + diff --git a/back/test/src/test/kar/karso/TestBase.java b/back/test/src/test/kar/karso/TestBase.java index 216f0db..7889b2e 100644 --- a/back/test/src/test/kar/karso/TestBase.java +++ b/back/test/src/test/kar/karso/TestBase.java @@ -1,36 +1,277 @@ package test.kar.karso; +import java.util.Map; -import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; - - - +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.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestExecutionExceptionHandler; +import org.kar.archidata.exception.RESTErrorResponseExeption; +import org.kar.archidata.model.GetToken; +import org.kar.archidata.util.ConfigBaseVariable; +import org.kar.archidata.util.JWTWrapper; +import org.kar.archidata.util.RESTApi; +import org.kar.karso.api.HealthCheck.HealthResult; +import org.kar.karso.model.DataGetToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.nimbusds.jwt.JWTClaimsSet; + +@ExtendWith(StepwiseExtension.class) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class TestBase { + final static Logger logger = LoggerFactory.getLogger(WebLauncherTest.class); + static WebLauncherTest webInterface = null; + static RESTApi api = null; + + + public void login(String login, String password) { + try { + GetToken token = api.post(GetToken.class, "users/get_token", DataGetToken.generate(login, "v1", "202515252", password)); + api.setToken(token.jwt()); + } catch (Exception ex) { + Assertions.fail("Can not get Authentication for '" + login + "' ==> " + ex.getMessage()); + } + } + + public void loginAdmin() { + login("karadmin", "adminA@666"); + } + + @BeforeAll + public static void configureWebServer() throws InterruptedException { + logger.info("configure server ..."); + webInterface = new WebLauncherTest(); + logger.info("Create DB"); + webInterface.generateDB(); + logger.info("Start REST (BEGIN)"); + webInterface.process(); + logger.info("Start REST (DONE)"); + api = new RESTApi(ConfigBaseVariable.apiAdress); + } + @AfterAll + public static void stopWebServer() throws InterruptedException { + logger.info("Kill the web server"); + webInterface = null; + // TODO: do it better... + } + + @Order(1) @Test - public void getData() throws IOException, InterruptedException { + //@RepeatedTest(10) + public void checkHealthCheck() throws Exception { + HealthResult result = api.get(HealthResult.class, "health_check"); + Assertions.assertEquals(result.value(), "alive and kicking"); + } + + @Order(2) + @Test + public void checkHealthCheckWrongAPI() throws Exception { + Assertions.assertThrows(RESTErrorResponseExeption.class, ()->api.get(HealthResult.class, "health_checks")); + } + + @Order(3) + @Test + public void firstUserConnect() throws Exception { + GetToken result = api.post(GetToken.class, "users/get_token", DataGetToken.generate("karadmin", "v1", "202515252", "adminA@666")); + String[] splitted = result.jwt().split("\\."); + Assertions.assertEquals(3, splitted.length); + String authorization = result.jwt(); + logger.debug(" validate token : " + authorization); + // Note with local access we get the internal key of the system. + JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); + // check the token is valid !!! (signed and coherent issuer... + Assertions.assertNotNull(ret); + // check userID + String userUID = ret.getSubject(); + long id = Long.parseLong(userUID); + Assertions.assertEquals(0, id); + String name = (String)ret.getClaim("login"); + Assertions.assertEquals("karadmin", name); + - HttpClient client = HttpClient.newHttpClient(); - //HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:5125/health_check" + System.currentTimeMillis())) - HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://gitea.atria-soft.org/")) - .GET().build(); - /* - HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:9200/indexname/typename/" + System.currentTimeMillis())) - .POST(HttpRequest.BodyPublishers.ofString(jsonString)).setHeader("Content-Type", "application/json").build(); -*/ - HttpResponse httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString()); + Object rowRight = ret.getClaim("right"); + Assertions.assertNotNull(rowRight); + Map> rights = (Map>) ret.getClaim("right"); + // Check if the element contain the basic keys: + Assertions.assertEquals(rights.size(), 1); + Assertions.assertTrue(rights.containsKey("karso")); + Map applRight = rights.get("karso"); + //logger.error("full right: {}", applRight); + Assertions.assertEquals(applRight.size(), 2); + Assertions.assertTrue(applRight.containsKey("ADMIN")); + Assertions.assertEquals(true, applRight.get("ADMIN")); + Assertions.assertTrue(applRight.containsKey("USER")); + Assertions.assertEquals(true, applRight.get("USER")); + + //logger.debug("request user: '{}' right: '{}' row='{}'", userUID, applRight, rowRight); + + //Assertions.assertEquals("eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9", splitted[0]); + //Assertions.assertEquals("eyJzdWIiOiIwIiwiYXBwbGljYXRpb24iOiJrYXJzbyIsImlzcyI6IkthckF1dGgiLCJyaWdodCI6eyJrYXJzbyI6eyJBRE1JTiI6dHJ1ZSwiVVNFUiI6dHJ1ZX19LCJsb2dpbiI6ImthcmFkbWluIiwiZXhwIjoxNjg0MTk5MTkzLCJpYXQiOjE2ODI3NTU0MjV9", splitted[1]); + // TODO ... Assertions.assertEquals("????", splitted[2]); + } - Assertions.assertEquals(httpResponse.statusCode(), 200); - System.out.println("========================================"); - System.out.println("retValue: " + httpResponse.statusCode()); - System.out.println("response: " + httpResponse.body()); - System.out.println("========================================"); + public void checkFail(String type, String urlOffset, int errorStatus) { + checkFail(type, urlOffset, errorStatus, null); + } + public void checkFail(String type, String urlOffset, int errorStatus, String data) { + logger.info("Test API: url={} urlOffset={}", type, urlOffset); + try { + if ("GET".equals(type)) { + api.get(String.class, urlOffset); + } else if ("POST".equals(type)) { + api.post(String.class, urlOffset, data); + } else if ("PUT".equals(type)) { + api.put(String.class, urlOffset, data); + } else if ("DELETE".equals(type)) { + api.delete(String.class, urlOffset); + } + Assertions.fail("Request on URL does not fail as expected: '" + type + "' url='" + urlOffset + "'"); + } catch (RESTErrorResponseExeption ex) { + if (errorStatus != ex.status) { + logger.error("Fail in test with the wrong return errors: {}", ex.toString()); + } + Assertions.assertEquals(errorStatus, ex.status); + } catch (Exception ex) { + logger.error("Unexpected throw error: {}", ex); + Assertions.fail("Unexpected throws..."); + } } -} \ No newline at end of file + public void checkWork(String type, String urlOffset) { + checkWork(type, urlOffset, null); + } + public void checkWork(String type, String urlOffset, String data) { + logger.info("Test API: url={} urlOffset={}", type, urlOffset); + try { + if ("GET".equals(type)) { + api.get(String.class, urlOffset); + } else if ("POST".equals(type)) { + api.post(String.class, urlOffset, data); + } else if ("PUT".equals(type)) { + api.put(String.class, urlOffset, data); + } else if ("DELETE".equals(type)) { + api.delete(String.class, urlOffset); + } + //Assertions.fail("Request on URL does not fail as expected: '" + type + "' url='" + urlOffset + "'"); + } catch (RESTErrorResponseExeption ex) { + Assertions.fail("Must not fail ... " + ex.toString()); + } catch (Exception ex) { + logger.error("Unexpected throw error: {}", ex); + Assertions.fail("Unexpected throws..."); + } + + } + @Order(4) + @Test + public void checkUnAuthorizedAPI() throws Exception { + // /application/ + checkFail("GET", "application/", 401); + checkFail("POST", "application/", 401, "{}"); + checkFail("PUT", "application/", 405, "{}"); // does not exist + checkFail("DELETE", "application/", 405); // does not exist + // /application/{id} + checkFail("GET", "application/0", 401); + checkFail("PUT", "application/0", 401, "{}"); + checkFail("POST", "application/0", 405, "{}"); + checkFail("DELETE", "application/0", 401); + // /application/{id}/* + checkFail("GET", "application/0/users", 401); + // /application/* + checkFail("GET", "application/small", 401); + checkFail("GET", "application/get_token", 401); + checkFail("GET", "application/return", 401); + + // /application_token/ section: + checkFail("GET", "application_token/0", 401); + checkFail("DELETE", "application_token/0/5", 401); + checkFail("DELETE", "application_token/0/create", 401); + + // /front/* + checkFail("GET", "front", 404); // no index in test section + // health check + checkWork("GET", "health_check"); + + // public_key (only application) + checkFail("GET", "public_key", 401); + checkFail("GET", "public_key/pem", 401); + + + // /right + checkFail("GET", "right", 401); + checkFail("POST", "right", 401, "{}"); + checkFail("GET", "right/0", 401); + checkFail("PUT", "right/0", 401, "{}"); + checkFail("DELETE", "right/0", 401); + + // /system_config + checkWork("GET", "system_config/is_sign_up_availlable"); + checkFail("GET", "system_config/key/skjdfhkjsdhfkjsh", 401); + checkFail("PUT", "system_config/key/skjdfhkjsdhfkjsh", 401, "{}"); + + // /users + checkFail("GET", "users", 401); + checkFail("GET", "users/0", 401); + checkFail("POST", "users/0/application/0/link", 401, "{}"); + checkFail("POST", "users/0/set_admin", 401, "{}"); + checkFail("POST", "users/0/set_blocked", 401, "{}"); + checkFail("POST", "users/create_new_user", 401, "{}"); + checkFail("GET", "users/me", 401, "{}"); + checkFail("POST", "users/password", 401, "{}"); + checkWork("GET", "users/check_login?login=karadmin"); + checkFail("GET", "users/check_login?login=jhkjhkjh", 404); + checkWork("GET", "users/check_email?email=admin@admin.ZZZ"); + checkFail("GET", "users/check_email?email=ksjhdkjfhskjdh", 404); + // not testable : get_token + + } + + @Order(5) + @Test + public void testMeWithToken() throws Exception { + this.loginAdmin(); + String result = api.get(String.class, "users/me"); + Assertions.assertEquals("{\"id\":0,\"login\":\"karadmin\"}", result); + + } + + +} + + +class StepwiseExtension implements ExecutionCondition, TestExecutionExceptionHandler { + @Override + public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext extensionContext) { + ExtensionContext.Namespace namespace = namespaceFor(extensionContext); + ExtensionContext.Store store = storeFor(extensionContext, namespace); + String value = store.get(StepwiseExtension.class, String.class); + return value == null ? ConditionEvaluationResult.enabled("No test failures in stepwise tests") : + ConditionEvaluationResult.disabled(String.format("Stepwise test disabled due to previous failure in '%s'", value)); + } + + @Override + public void handleTestExecutionException(ExtensionContext extensionContext, Throwable throwable) throws Throwable { + ExtensionContext.Namespace namespace = namespaceFor(extensionContext); + ExtensionContext.Store store = storeFor(extensionContext, namespace); + store.put(StepwiseExtension.class, extensionContext.getDisplayName()); + throw throwable; + } + + private ExtensionContext.Namespace namespaceFor(ExtensionContext extensionContext){ + return ExtensionContext.Namespace.create(StepwiseExtension.class, extensionContext.getParent()); + } + + + private ExtensionContext.Store storeFor(ExtensionContext extensionContext, ExtensionContext.Namespace namespace){ + return extensionContext.getParent().get().getStore(namespace); + } +} diff --git a/back/test/src/test/kar/karso/WebLauncherTest.java b/back/test/src/test/kar/karso/WebLauncherTest.java new file mode 100755 index 0000000..3d1c4f1 --- /dev/null +++ b/back/test/src/test/kar/karso/WebLauncherTest.java @@ -0,0 +1,25 @@ + +package test.kar.karso; + +import org.kar.archidata.util.ConfigBaseVariable; +import org.kar.karso.WebLauncher; +import org.kar.karso.util.ConfigVariable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebLauncherTest extends WebLauncher { + final Logger logger = LoggerFactory.getLogger(WebLauncherTest.class); + public WebLauncherTest() { + logger.debug("Configure REST system"); + // for local test: + ConfigBaseVariable.apiAdress = "http://127.0.0.1:12345/test/api/"; + ConfigBaseVariable.dbPort = "3306"; + // create a unique key for test ==> not retrieve the token every load... + ConfigVariable.uuid_for_key_generation = "lkjlkjlkjlmkjqmwlsdkjqfsdlkf,nmQLSDK,NFMQLKSdjmlKQJSDMLQK,S;ndmLQKZNERMA,ÉL"; + // for the test we a in memory sqlite.. + ConfigBaseVariable.dbType = "sqlite"; + ConfigBaseVariable.dbHost = "memory"; + // for test we need to connect all time the DB + ConfigBaseVariable.dbKeepConnected = "true"; + } +}