[DEV] security, test-unit, coverage, logger

This commit is contained in:
Edouard DUPIN 2023-04-30 23:54:47 +02:00
parent 7917d239dc
commit d10897a086
20 changed files with 709 additions and 316 deletions

View File

@ -23,5 +23,12 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="out/maven/test-classes" path="test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="out/maven/classes"/>
</classpath>

View File

@ -32,12 +32,12 @@
</natures>
<filteredResources>
<filter>
<id>1647191868592</id>
<id>1682721079869</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId>
<artifactId>karso</artifactId>
<version>0.4.1</version>
<properties>
<!--
<modelVersion>4.0.0</modelVersion>
<groupId>org.kar</groupId>
<artifactId>karso</artifactId>
<version>0.4.1</version>
<properties>
<!--
<jaxb.version>2.3.1</jaxb.version>
<istack.version>4.1.1</istack.version>
-->
<maven.compiler.version>3.1</maven.compiler.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.dependency.version>3.1.1</maven.dependency.version>
</properties>
<repositories>
@ -21,27 +20,30 @@
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.3.5</version>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test/src</testSourceDirectory>
<directory>${project.basedir}/out/maven/</directory>
<directory>${project.basedir}/out/maven/</directory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -53,14 +55,13 @@
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>org.kar.karso.WebLauncher</mainClass>
</configuration>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<mainClass>org.kar.karso.WebLauncher</mainClass>
</configuration>
</plugin>
<!-- Create the source bundle -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -81,41 +82,68 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>fully.qualified.MainClass</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<!-- Create coverage -->
<!--
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<version>0.8.10</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<!--<configuration>
<destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
<propertyName>surefireArgLine</propertyName>
</configuration>-->
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals><!--
<configuration>
<dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
<outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
</configuration>-->
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.50</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
-->
<!-- Java-doc generation for stand-alone site -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -126,49 +154,12 @@
<nohelp>true</nohelp>
</configuration>
</plugin>
<!-- Check the style of the code -->
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<configLocation>CheckStyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<failsOnError>true</failsOnError>
<includeTestSourceDirectory>true</includeTestSourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.12.2</version>
<configuration>
<encoding>UTF-8</encoding>
<lineEnding>LF</lineEnding>
<configFile>Formatter.xml</configFile>
<directories>
<directory>src/</directory>
<directory>test/src</directory>
</directories>
<includes>
<include>**/*.java</include>
</includes>
<excludes>
<exclude>module-info.java</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>validate</goal>
</goals>
</execution>
</executions>
</plugin>
-->
</plugins>
<testResources>
<testResource>
<directory>${basedir}/test/resources</directory>
</testResource>
</testResources>
</build>
<!-- Generate Java-docs As Part Of Project Reports -->
<reporting>
@ -183,4 +174,4 @@
</plugin>
</plugins>
</reporting>
</project>
</project>

View File

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

View File

@ -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 {

View File

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

View File

@ -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<Long> 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<Application> 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<Application> 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<Long> 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<ApplicationSmall> getApplicationsSmall(@Context SecurityContext sc) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal();
System.out.println("getApplications");
logger.debug("getApplications");
List<Application> tmp = SqlWrapper.gets(Application.class, false);
List<Long> regular = this.getUserListOfApplication(gc.userByToken.id);
List<ApplicationSmall> 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);
}
}

View File

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

View File

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

View File

@ -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() {

View File

@ -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());

View File

@ -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<Right> 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}")

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -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 + "'"));
}
}

View File

@ -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

View File

@ -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<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
Object rowRight = ret.getClaim("right");
Assertions.assertNotNull(rowRight);
Map<String, Map<String,Object>> rights = (Map<String, Map<String,Object>>) ret.getClaim("right");
// Check if the element contain the basic keys:
Assertions.assertEquals(rights.size(), 1);
Assertions.assertTrue(rights.containsKey("karso"));
Map<String, Object> 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...");
}
}
}
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);
}
}

View File

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