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

22
pom.xml
View File

@ -1,9 +1,9 @@
<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>kar</groupId>
<groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId>
<version>0.1.0</version>
<properties>
<version>0.1.2</version>
<properties>
<jaxrs.version>2.1</jaxrs.version>
<jersey.version>2.32</jersey.version>
<jaxb.version>2.3.1</jaxb.version>
@ -16,6 +16,22 @@
<maven.dependency.version>3.1.1</maven.dependency.version>
</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>
<dependencies>

View File

@ -34,6 +34,7 @@ import org.kar.archidata.annotation.SQLDefault;
public class SqlWrapper {
public static class ExceptionDBInterface extends Exception {
private static final long serialVersionUID = 1L;
public int errorID;
ExceptionDBInterface(int errorId, String message) {
super(message);
@ -692,6 +693,18 @@ public class SqlWrapper {
ps.setShort(iii++, (Short)value);
} else if (value.getClass() == Byte.class) {
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 {
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 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.util.List;
import javax.annotation.security.PermitAll;
import javax.ws.rs.*;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import org.kar.archidata.annotation.security.PermitAll;
public class FrontGeneric {

View File

@ -1,28 +1,25 @@
package org.kar.archidata.filter;
import java.lang.reflect.Method;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.kar.archidata.annotation.security.DenyAll;
import org.kar.archidata.annotation.security.PermitAll;
import org.kar.archidata.annotation.security.RolesAllowed;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.QueryParam;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.PathSegment;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
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.UserSmall;
import org.kar.archidata.util.JWTWrapper;
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:
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());
return;
}
// Get the Authorization header from the request
@ -133,10 +129,14 @@ public class AuthenticationFilter implements ContainerRequestFilter {
try {
user = validateToken(token);
} catch (Exception e) {
System.out.println("Fail to validate token: " + e.getMessage());
abortWithUnauthorized(requestContext);
return;
}
if (user == null) {
System.out.println("get a NULL user ...");
abortWithUnauthorized(requestContext);
return;
}
// create the security context model:
String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
@ -183,7 +183,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
private User validateToken(String authorization) throws Exception {
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...
if (ret == null) {
System.out.println("The token is not valid: '" + authorization + "'");

View File

@ -36,7 +36,7 @@ public class User {
@SQLNotNull
@SQLComment("Primary key of the base")
public Long id = null;
@SQLLimitSize(256)
@SQLLimitSize(128)
public String login = null;
public Timestamp lastConnection = null;
@ -49,5 +49,9 @@ public class User {
@SQLDefault("'0'")
@SQLNotNull
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;
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() {
String out = System.getenv("DATA_TMP_FOLDER");
if (out == null) {
if (tmpDataFolder == null) {
return "/application/data/tmp";
}
return out;
return tmpDataFolder;
}
public static String getMediaDataFolder() {
String out = System.getenv("DATA_FOLDER");
if (out == null) {
if (dataFolder == null) {
return "/application/data/media";
}
return out;
return dataFolder;
}
public static String getDBHost() {
String out = System.getenv("DB_HOST");
if (out == null) {
if (dbHost == null) {
return "localhost";
}
return out;
return dbHost;
}
public static String getDBPort() {
String out = System.getenv("DB_PORT");
if (out == null) {
return "80";
//return "17036";
if (dbPort == null) {
return "3306";
}
return out;
return dbPort;
}
public static String getDBLogin() {
String out = System.getenv("DB_USER");
if (out == null) {
if (dbUser == null) {
return "root";
}
return out;
return dbUser;
}
public static String getDBPassword() {
String out = System.getenv("DB_PASSWORD");
if (out == null) {
return "archidata_password";
if (dbPassword == null) {
return "base_db_password";
}
return out;
return dbPassword;
}
public static String getDBName() {
String out = System.getenv("DB_DATABASE");
if (out == null) {
if (bdDatabase == null) {
return "unknown";
}
return out;
return bdDatabase;
}
public static String getlocalAddress() {
String out = System.getenv("API_ADDRESS");
if (out == null) {
if (apiAdress == null) {
return "http://0.0.0.0:80/api/";
}
return out;
return apiAdress;
}
public static String getSSOAddress() {
String out = System.getenv("SSO_ADDRESS");
if (out == null) {
return "http://sso_host/karauth/api/";
//return "http://192.168.1.156/karauth/api/";
if (ssoAdress == null) {
//return "http://sso_host/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.
* @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) {
System.out.println("JWT private key is not present !!!");
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 {
// Create RSA-signer with the private key
JWSSigner signer = new RSASSASigner(rsaJWK);
@ -122,9 +128,10 @@ public class JWTWrapper {
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject(Long.toString(userID))
.claim("login", userLogin)
.claim("application", application)
.issuer(isuer)
.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();
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;
}
public static JWTClaimsSet validateToken(String signedToken, String isuer) {
public static JWTClaimsSet validateToken(String signedToken, String isuer, String application) {
if (rsaPublicJWK == null) {
System.out.println("JWT public key is not present !!!");
return null;
@ -161,6 +168,9 @@ public class JWTWrapper {
System.out.println("JWT issuer is wong: '" + isuer + "' != '" + signedJWT.getJWTClaimsSet().getIssuer() + "'" );
return null;
}
if (application != null) {
// TODO: verify the token is used for the correct application.
}
// the element must be validated outside ...
//System.out.println("JWT token is verified 'alice' =?= '" + signedJWT.getJWTClaimsSet().getSubject() + "'");
//System.out.println("JWT token isuer 'https://c2id.com' =?= '" + signedJWT.getJWTClaimsSet().getIssuer() + "'");