upgrade security and token management (add application and corect expiration date)

This commit is contained in:
Edouard DUPIN 2022-11-26 00:24:03 +01:00
parent 09ff403d9a
commit 044889129a
12 changed files with 147 additions and 52 deletions

View File

@ -15,3 +15,12 @@ generic interface for all KAR web application
Read instruction for tocken in ~/.m2/setting.xml
release:
mvn install
mvn deploy

20
pom.xml
View File

@ -1,8 +1,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"> <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> <modelVersion>4.0.0</modelVersion>
<groupId>kar</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.1.0</version> <version>0.1.2</version>
<properties> <properties>
<jaxrs.version>2.1</jaxrs.version> <jaxrs.version>2.1</jaxrs.version>
<jersey.version>2.32</jersey.version> <jersey.version>2.32</jersey.version>
@ -16,6 +16,22 @@
<maven.dependency.version>3.1.1</maven.dependency.version> <maven.dependency.version>3.1.1</maven.dependency.version>
</properties> </properties>
<repositories>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
</repositories>
<distributionManagement>
<repository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</repository>
<snapshotRepository>
<id>gitea</id>
<url>https://gitea.atria-soft.org/api/packages/kangaroo-and-rabbit/maven</url>
</snapshotRepository>
</distributionManagement>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>

View File

@ -34,6 +34,7 @@ import org.kar.archidata.annotation.SQLDefault;
public class SqlWrapper { public class SqlWrapper {
public static class ExceptionDBInterface extends Exception { public static class ExceptionDBInterface extends Exception {
private static final long serialVersionUID = 1L;
public int errorID; public int errorID;
ExceptionDBInterface(int errorId, String message) { ExceptionDBInterface(int errorId, String message) {
super(message); super(message);
@ -692,6 +693,18 @@ public class SqlWrapper {
ps.setShort(iii++, (Short)value); ps.setShort(iii++, (Short)value);
} else if (value.getClass() == Byte.class) { } else if (value.getClass() == Byte.class) {
ps.setByte(iii++, (Byte)value); ps.setByte(iii++, (Byte)value);
} else if (value.getClass() == Float.class) {
ps.setFloat(iii++, (Float)value);
} else if (value.getClass() == Double.class) {
ps.setDouble(iii++, (Double)value);
} else if (value.getClass() == Boolean.class) {
ps.setBoolean(iii++, (Boolean)value);
} else if (value.getClass() == Boolean.class) {
ps.setBoolean(iii++, (Boolean)value);
} else if (value.getClass() == Timestamp.class) {
ps.setTimestamp(iii++, (Timestamp)value);
} else if (value.getClass() == Date.class) {
ps.setDate(iii++, (Date)value);
} else { } else {
throw new Exception("Not manage type ==> need to add it ..."); throw new Exception("Not manage type ==> need to add it ...");
} }

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package org.kar.archidata.annotation; package org.kar.archidata.annotation.security;
import javax.ws.rs.NameBinding; import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;

View File

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

View File

@ -3,13 +3,14 @@ package org.kar.archidata.api;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import javax.annotation.security.PermitAll;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.ResponseBuilder;
import org.kar.archidata.annotation.security.PermitAll;
public class FrontGeneric { public class FrontGeneric {

View File

@ -1,28 +1,25 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import javax.annotation.security.DenyAll; import org.kar.archidata.annotation.security.DenyAll;
import javax.annotation.security.PermitAll; import org.kar.archidata.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed; import org.kar.archidata.annotation.security.RolesAllowed;
import javax.annotation.Priority; import javax.annotation.Priority;
import javax.ws.rs.Priorities; import javax.ws.rs.Priorities;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter; import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo; import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider; import javax.ws.rs.ext.Provider;
import org.kar.archidata.UserDB; import org.kar.archidata.UserDB;
import org.kar.archidata.annotation.PermitTokenInURI; import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.model.User; import org.kar.archidata.model.User;
import org.kar.archidata.model.UserSmall;
import org.kar.archidata.util.JWTWrapper; import org.kar.archidata.util.JWTWrapper;
import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet;
@ -68,10 +65,9 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
// this is a security guard, all the API must define their access level: // this is a security guard, all the API must define their access level:
if(!method.isAnnotationPresent(RolesAllowed.class)) { if(!method.isAnnotationPresent(RolesAllowed.class)) {
System.out.println(" ==> missin @RolesAllowed " + requestContext.getUriInfo().getPath()); System.out.println(" ==> missing @RolesAllowed " + requestContext.getUriInfo().getPath());
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build()); requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Access ILLEGAL !!!").build());
return; return;
} }
// Get the Authorization header from the request // Get the Authorization header from the request
@ -133,10 +129,14 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try { try {
user = validateToken(token); user = validateToken(token);
} catch (Exception e) { } catch (Exception e) {
System.out.println("Fail to validate token: " + e.getMessage());
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return;
} }
if (user == null) { if (user == null) {
System.out.println("get a NULL user ...");
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return;
} }
// create the security context model: // create the security context model:
String scheme = requestContext.getUriInfo().getRequestUri().getScheme(); String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
@ -183,7 +183,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
private User validateToken(String authorization) throws Exception { private User validateToken(String authorization) throws Exception {
System.out.println(" validate token : " + authorization); System.out.println(" validate token : " + authorization);
JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth"); JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null);
// check the token is valid !!! (signed and coherent issuer... // check the token is valid !!! (signed and coherent issuer...
if (ret == null) { if (ret == null) {
System.out.println("The token is not valid: '" + authorization + "'"); System.out.println("The token is not valid: '" + authorization + "'");

View File

@ -36,7 +36,7 @@ public class User {
@SQLNotNull @SQLNotNull
@SQLComment("Primary key of the base") @SQLComment("Primary key of the base")
public Long id = null; public Long id = null;
@SQLLimitSize(256) @SQLLimitSize(128)
public String login = null; public String login = null;
public Timestamp lastConnection = null; public Timestamp lastConnection = null;
@ -49,5 +49,9 @@ public class User {
@SQLDefault("'0'") @SQLDefault("'0'")
@SQLNotNull @SQLNotNull
public boolean removed = false; public boolean removed = false;
@Override
public String toString() {
return "User [login=" + login + ", last=" + lastConnection + ", admin=" + admin + "]";
}
} }

View File

@ -1,78 +1,77 @@
package org.kar.archidata.util; package org.kar.archidata.util;
public class ConfigBaseVariable { public class ConfigBaseVariable {
static public String tmpDataFolder = System.getenv("DATA_TMP_FOLDER");
static public String dataFolder = System.getenv("DATA_FOLDER");
static public String dbHost = System.getenv("DB_HOST");
static public String dbPort = System.getenv("DB_PORT");
static public String dbUser = System.getenv("DB_USER");
static public String dbPassword = System.getenv("DB_PASSWORD");
static public String bdDatabase = System.getenv("DB_DATABASE");
static public String apiAdress = System.getenv("API_ADDRESS");
static public String ssoAdress = System.getenv("SSO_ADDRESS");
public static String getTmpDataFolder() { public static String getTmpDataFolder() {
String out = System.getenv("DATA_TMP_FOLDER"); if (tmpDataFolder == null) {
if (out == null) {
return "/application/data/tmp"; return "/application/data/tmp";
} }
return out; return tmpDataFolder;
} }
public static String getMediaDataFolder() { public static String getMediaDataFolder() {
String out = System.getenv("DATA_FOLDER"); if (dataFolder == null) {
if (out == null) {
return "/application/data/media"; return "/application/data/media";
} }
return out; return dataFolder;
} }
public static String getDBHost() { public static String getDBHost() {
String out = System.getenv("DB_HOST"); if (dbHost == null) {
if (out == null) {
return "localhost"; return "localhost";
} }
return out; return dbHost;
} }
public static String getDBPort() { public static String getDBPort() {
String out = System.getenv("DB_PORT"); if (dbPort == null) {
if (out == null) { return "3306";
return "80";
//return "17036";
} }
return out; return dbPort;
} }
public static String getDBLogin() { public static String getDBLogin() {
String out = System.getenv("DB_USER"); if (dbUser == null) {
if (out == null) {
return "root"; return "root";
} }
return out; return dbUser;
} }
public static String getDBPassword() { public static String getDBPassword() {
String out = System.getenv("DB_PASSWORD"); if (dbPassword == null) {
if (out == null) { return "base_db_password";
return "archidata_password";
} }
return out; return dbPassword;
} }
public static String getDBName() { public static String getDBName() {
String out = System.getenv("DB_DATABASE"); if (bdDatabase == null) {
if (out == null) {
return "unknown"; return "unknown";
} }
return out; return bdDatabase;
} }
public static String getlocalAddress() { public static String getlocalAddress() {
String out = System.getenv("API_ADDRESS"); if (apiAdress == null) {
if (out == null) {
return "http://0.0.0.0:80/api/"; return "http://0.0.0.0:80/api/";
} }
return out; return apiAdress;
} }
public static String getSSOAddress() { public static String getSSOAddress() {
String out = System.getenv("SSO_ADDRESS"); if (ssoAdress == null) {
if (out == null) { //return "http://sso_host/api/";
return "http://sso_host/karauth/api/"; return "http://192.168.1.156/karauth/api/";
//return "http://192.168.1.156/karauth/api/";
} }
return out; return ssoAdress;
} }
} }

View File

@ -110,11 +110,17 @@ public class JWTWrapper {
* @param timeOutInMunites Expiration of the token. * @param timeOutInMunites Expiration of the token.
* @return the encoded token * @return the encoded token
*/ */
public static String generateJWToken(long userID, String userLogin, String isuer, int timeOutInMunites) { public static String generateJWToken(long userID, String userLogin, String isuer, String application, int timeOutInMunites) {
if (rsaJWK == null) { if (rsaJWK == null) {
System.out.println("JWT private key is not present !!!"); System.out.println("JWT private key is not present !!!");
return null; return null;
} }
/*
System.out.println(" ===> expire in : " + timeOutInMunites);
System.out.println(" ===>" + new Date().getTime());
System.out.println(" ===>" + new Date(new Date().getTime()));
System.out.println(" ===>" + new Date(new Date().getTime() - 60 * timeOutInMunites * 1000));
*/
try { try {
// Create RSA-signer with the private key // Create RSA-signer with the private key
JWSSigner signer = new RSASSASigner(rsaJWK); JWSSigner signer = new RSASSASigner(rsaJWK);
@ -122,9 +128,10 @@ public class JWTWrapper {
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder() JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject(Long.toString(userID)) .subject(Long.toString(userID))
.claim("login", userLogin) .claim("login", userLogin)
.claim("application", application)
.issuer(isuer) .issuer(isuer)
.issueTime(new Date()) .issueTime(new Date())
.expirationTime(new Date(new Date().getTime() + 60 * timeOutInMunites * 1000 /* millisecond */)) .expirationTime(new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */)) // Do not ask why we need a "-" here ... this have no meaning
.build(); .build();
SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet); SignedJWT signedJWT = new SignedJWT(new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT)/*.keyID(rsaJWK.getKeyID())*/.build(), claimsSet);
@ -139,7 +146,7 @@ public class JWTWrapper {
return null; return null;
} }
public static JWTClaimsSet validateToken(String signedToken, String isuer) { public static JWTClaimsSet validateToken(String signedToken, String isuer, String application) {
if (rsaPublicJWK == null) { if (rsaPublicJWK == null) {
System.out.println("JWT public key is not present !!!"); System.out.println("JWT public key is not present !!!");
return null; return null;
@ -161,6 +168,9 @@ public class JWTWrapper {
System.out.println("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'" ); System.out.println("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'" );
return null; return null;
} }
if (application != null) {
// TODO: verify the token is used for the correct application.
}
// the element must be validated outside ... // the element must be validated outside ...
//System.out.println("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'"); //System.out.println("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'");
//System.out.println("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'"); //System.out.println("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'");