base of new securisation model
All checks were successful
WEB karideo and rabbit/archidata/pipeline/head This commit looks good

This commit is contained in:
Edouard DUPIN 2023-01-28 00:15:03 +01:00
parent 949fc0b05c
commit 50cb92703b
12 changed files with 240 additions and 116 deletions

View File

@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>kangaroo-and-rabbit</groupId> <groupId>kangaroo-and-rabbit</groupId>
<artifactId>archidata</artifactId> <artifactId>archidata</artifactId>
<version>0.2.6</version> <version>0.3.1</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>

View File

@ -0,0 +1,5 @@
package org.kar.archidata;
public class MigrationAction {
}

View File

@ -616,6 +616,45 @@ public class SqlWrapper {
public static <T> List<T> getsWhere(Class<T> clazz, List<WhereCondition> condition, boolean full, Integer linit) throws Exception { public static <T> List<T> getsWhere(Class<T> clazz, List<WhereCondition> condition, boolean full, Integer linit) throws Exception {
return getsWhere(clazz, condition, null, full, linit); return getsWhere(clazz, condition, null, full, linit);
} }
public static void whereAppendQuery(StringBuilder query, String tableName, List<WhereCondition> condition, boolean exclude_deleted) throws ExceptionDBInterface {
// Check if we have a condition to generate
if (condition == null || condition.size() == 0) {
return;
}
query.append(" WHERE ");
boolean first = true;
for (WhereCondition elem : condition) {
if (first) {
first = false;
} else {
query.append(" AND ");
}
query.append(tableName);
query.append(".");
query.append(elem.key());
query.append(" ");
query.append(elem.comparator());
query.append(" ?");
}
if (exclude_deleted) {
if (!first) {
query.append(" AND ");
}
query.append(tableName);
query.append(".deleted = false ");
}
}
public static void whereInjectValue(PreparedStatement ps, List<WhereCondition> condition) throws Exception {
// Check if we have a condition to generate
if (condition == null || condition.size() == 0) {
return;
}
int iii = 1;
for (WhereCondition elem : condition) {
addElement(ps, elem.Value(), iii++);
}
}
public static <T> List<T> getsWhere(Class<T> clazz, List<WhereCondition> condition, String orderBy, boolean full, Integer linit) throws Exception { public static <T> List<T> getsWhere(Class<T> clazz, List<WhereCondition> condition, String orderBy, boolean full, Integer linit) throws Exception {
DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig); DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
List<T> outs = new ArrayList<>(); List<T> outs = new ArrayList<>();
@ -659,24 +698,7 @@ public class SqlWrapper {
query.append(" FROM `"); query.append(" FROM `");
query.append(tableName); query.append(tableName);
query.append("` "); query.append("` ");
query.append(" WHERE "); whereAppendQuery(query, tableName, condition, true);
if (condition.size() == 0) {
throw new ExceptionDBInterface(4575643, tableName + " ==> request where without parameters");
}
boolean first = true;
for (WhereCondition elem : condition) {
if (first) {
first = false;
} else {
query.append(" AND ");
}
query.append(tableName);
query.append(".");
query.append(elem.key());
query.append(" ");
query.append(elem.comparator());
query.append(" ?");
}
if (orderBy != null && orderBy.length() >= 1) { if (orderBy != null && orderBy.length() >= 1) {
query.append(" ORDER BY "); query.append(" ORDER BY ");
//query.append(tableName); //query.append(tableName);
@ -695,10 +717,7 @@ public class SqlWrapper {
//System.out.println("generate the query: '" + query.toString() + "'"); //System.out.println("generate the query: '" + query.toString() + "'");
// prepare the request: // prepare the request:
PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS); PreparedStatement ps = entry.connection.prepareStatement(query.toString(), Statement.RETURN_GENERATED_KEYS);
int iii = 1; whereInjectValue(ps, condition);
for (WhereCondition elem : condition) {
addElement(ps, elem.Value(), iii++);
}
// execute the request // execute the request
ResultSet rs = ps.executeQuery(); ResultSet rs = ps.executeQuery();
while (rs.next()) { while (rs.next()) {
@ -1000,35 +1019,51 @@ public class SqlWrapper {
public static void delete(Class<?> clazz, long id) throws Exception { public static void delete(Class<?> clazz, long id) throws Exception {
// TODO: I am not sure this is a real good idea. // TODO: I am not sure this is a real good idea.
} }
public static void setDelete(Class<?> clazz, long id) throws Exception { public static int setDelete(Class<?> clazz, long id) throws Exception {
return setDeleteWhere(clazz, List.of(
new WhereCondition("id", "=", id)
));
}
public static int setDeleteWhere(Class<?> clazz, List<WhereCondition> condition) throws Exception {
String tableName = getTableName(clazz); String tableName = getTableName(clazz);
DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig); DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
String query = "UPDATE `" + tableName + "` SET `modify_date`=now(3), `deleted`=true WHERE `id` = ?"; StringBuilder query = new StringBuilder();
query.append("UPDATE `");
query.append(tableName);
query.append("` SET `modify_date`=now(3), `deleted`=true ");
whereAppendQuery(query, tableName, condition, false);
try { try {
PreparedStatement ps = entry.connection.prepareStatement(query); PreparedStatement ps = entry.connection.prepareStatement(query.toString());
int iii = 1; whereInjectValue(ps, condition);
ps.setLong(iii++, id); int affectedRows = ps.executeUpdate();
ps.executeUpdate(); return affectedRows;
} catch (SQLException ex) {
ex.printStackTrace();
throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
} finally { } finally {
entry.close(); entry.close();
entry = null; entry = null;
} }
} }
public static void unsetDelete(Class<?> clazz, long id) throws Exception {
public static int unsetDelete(Class<?> clazz, long id) throws Exception {
return unsetDeleteWhere(clazz, List.of(
new WhereCondition("id", "=", id)
));
}
public static int unsetDeleteWhere(Class<?> clazz, List<WhereCondition> condition) throws Exception {
String tableName = getTableName(clazz); String tableName = getTableName(clazz);
DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig); DBEntry entry = new DBEntry(GlobalConfiguration.dbConfig);
String query = "UPDATE `" + tableName + "` SET `modify_date`=now(3), `deleted`=false WHERE `id` = ?"; StringBuilder query = new StringBuilder();
query.append("UPDATE `");
query.append(tableName);
query.append("` SET `modify_date`=now(3), `deleted`=false ");
whereAppendQuery(query, tableName, condition, false);
try { try {
PreparedStatement ps = entry.connection.prepareStatement(query); PreparedStatement ps = entry.connection.prepareStatement(query.toString());
int iii = 1; whereInjectValue(ps, condition);
ps.setLong(iii++, id); int affectedRows = ps.executeUpdate();
ps.executeUpdate(); return affectedRows;
} catch (SQLException ex) {
ex.printStackTrace();
throw new ExceptionDBInterface(500, "SQL error: " + ex.getMessage());
} finally { } finally {
entry.close(); entry.close();
entry = null; entry = null;
@ -1228,4 +1263,5 @@ public class SqlWrapper {
return ((SQLLimitSize) annotation[0]).value(); return ((SQLLimitSize) annotation[0]).value();
} }
} }

View File

@ -20,6 +20,7 @@ import javax.ws.rs.ext.Provider;
import org.kar.archidata.UserDB; import org.kar.archidata.UserDB;
import org.kar.archidata.annotation.security.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.UserByToken;
import org.kar.archidata.util.JWTWrapper; import org.kar.archidata.util.JWTWrapper;
import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.JWTClaimsSet;
@ -40,6 +41,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
private ResourceInfo resourceInfo; private ResourceInfo resourceInfo;
private static final String AUTHENTICATION_SCHEME = "Yota"; private static final String AUTHENTICATION_SCHEME = "Yota";
private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota";
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) throws IOException {
@ -84,63 +86,68 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
} }
} }
//System.out.println("authorizationHeader: " + authorizationHeader); // System.out.println("authorizationHeader: " + authorizationHeader);
boolean isApplicationToken = isApplicationTokenBasedAuthentication(authorizationHeader);
boolean isJwtToken = isTokenBasedAuthentication(authorizationHeader);
/* // Validate the Authorization header data Model "Yota jwt.to.ken" "Zota tokenId:hash(token)"
System.out.println(" -------------------------------"); if (!isApplicationToken && !isJwtToken) {
// this get the parameters inside the pre-parsed element in the request ex: @Path("thumbnail/{id}") generate a map with "id"
MultivaluedMap<String, String> pathparam = requestContext.getUriInfo().getPathParameters();
for (Entry<String, List<String>> item: pathparam.entrySet()) {
System.out.println(" param: " + item.getKey() + " ==>" + item.getValue());
}
System.out.println(" -------------------------------");
// need to add "@QueryParam("p") String token, " in the model
//MultivaluedMap<String, String> quaryparam = requestContext.getUriInfo().getQueryParameters();
for (Entry<String, List<String>> item: quaryparam.entrySet()) {
System.out.println(" query: " + item.getKey() + " ==>" + item.getValue());
}
System.out.println(" -------------------------------");
List<PathSegment> segments = requestContext.getUriInfo().getPathSegments();
for (final PathSegment item: segments) {
System.out.println(" query: " + item.getPath() + " ==>" + item.getMatrixParameters());
}
System.out.println(" -------------------------------");
MultivaluedMap<String, String> headers = requestContext.getHeaders();
for (Entry<String, List<String>> item: headers.entrySet()) {
System.out.println(" headers: " + item.getKey() + " ==>" + item.getValue());
}
System.out.println(" -------------------------------");
*/
// Validate the Authorization header data Model "Yota userId:token"
if (!isTokenBasedAuthentication(authorizationHeader)) {
System.out.println("REJECTED unauthorized: " + requestContext.getUriInfo().getPath()); System.out.println("REJECTED unauthorized: " + requestContext.getUriInfo().getPath());
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return; return;
} }
// check JWT token (basic:)
// Extract the token from the Authorization header (Remove "Yota ")
String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
System.out.println("token: " + token);
User user = null; User user = null;
try { UserByToken userByToken = null;
user = validateToken(token); if (isJwtToken) {
} catch (Exception e) { // Extract the token from the Authorization header (Remove "Yota ")
System.out.println("Fail to validate token: " + e.getMessage()); String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
abortWithUnauthorized(requestContext); //System.out.println("token: " + token);
return; try {
} user = validateJwtToken(token);
if (user == null) { } catch (Exception e) {
System.out.println("get a NULL user ..."); System.out.println("Fail to validate token: " + e.getMessage());
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return; return;
}
if (user == null) {
System.out.println("get a NULL user ...");
abortWithUnauthorized(requestContext);
return;
}
// We are in transition phase the user element will be removed
userByToken = new UserByToken();
userByToken.id = user.id;
userByToken.name = user.login;
userByToken.parentId = null;
userByToken.type = UserByToken.TYPE_USER;
if (user.removed || user.blocked) {
userByToken.type = null;
} else if (user.admin) {
userByToken.right.put("ADMIN", true);
userByToken.right.put("USER", true);
} else {
userByToken.right.put("USER", true);
}
} else {
// Extract the token from the Authorization header (Remove "Zota ")
String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim();
//System.out.println("token: " + token);
try {
userByToken = validateToken(token);
} catch (Exception e) {
System.out.println("Fail to validate token: " + e.getMessage());
abortWithUnauthorized(requestContext);
return;
}
if (userByToken == null) {
System.out.println("get a NULL application ...");
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();
MySecurityContext userContext = new MySecurityContext(user, scheme); MySecurityContext userContext = new MySecurityContext(user, userByToken, scheme);
// retrieve the allowed right: // retrieve the allowed right:
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class); RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
List<String> roles = Arrays.asList(rolesAnnotation.value()); List<String> roles = Arrays.asList(rolesAnnotation.value());
@ -152,7 +159,6 @@ public class AuthenticationFilter implements ContainerRequestFilter {
break; break;
} }
} }
//Is user valid? //Is user valid?
if( ! haveRight) { if( ! haveRight) {
System.out.println("REJECTED not enought right : " + requestContext.getUriInfo().getPath() + " require: " + roles); System.out.println("REJECTED not enought right : " + requestContext.getUriInfo().getPath() + " require: " + roles);
@ -160,7 +166,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
return; return;
} }
requestContext.setSecurityContext(userContext); requestContext.setSecurityContext(userContext);
System.out.println("Get local user : " + user); // System.out.println("Get local user : " + user + " / " + userByToken);
} }
private boolean isTokenBasedAuthentication(String authorizationHeader) { private boolean isTokenBasedAuthentication(String authorizationHeader) {
@ -170,6 +176,14 @@ public class AuthenticationFilter implements ContainerRequestFilter {
return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " "); return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
} }
private boolean isApplicationTokenBasedAuthentication(String authorizationHeader) {
// Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace
// The authentication scheme comparison must be case-insensitive
return authorizationHeader != null && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_TOKEN_SCHEME.toLowerCase() + " ");
}
private void abortWithUnauthorized(ContainerRequestContext requestContext) { private void abortWithUnauthorized(ContainerRequestContext requestContext) {
// Abort the filter chain with a 401 status code response // Abort the filter chain with a 401 status code response
@ -181,7 +195,12 @@ public class AuthenticationFilter implements ContainerRequestFilter {
.build()); .build());
} }
private User validateToken(String authorization) throws Exception { protected UserByToken validateToken(String authorization) throws Exception {
System.out.println("dsfgsdgsdfgsdgf");
return null;
}
// must be override to be good implemenres
protected User validateJwtToken(String authorization) throws Exception {
System.out.println(" validate token : " + authorization); System.out.println(" validate token : " + authorization);
JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); 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...

View File

@ -1,15 +1,18 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import org.kar.archidata.model.User; import org.kar.archidata.model.User;
import org.kar.archidata.model.UserByToken;
import java.security.Principal; import java.security.Principal;
public class GenericContext implements Principal { public class GenericContext implements Principal {
public User user; public User user;
public UserByToken userByToken;
public GenericContext(User user) { public GenericContext(User user, UserByToken userByToken) {
this.user = user; this.user = user;
this.userByToken = userByToken;
} }
@Override @Override

View File

@ -2,6 +2,7 @@ package org.kar.archidata.filter;
import org.kar.archidata.model.User; import org.kar.archidata.model.User;
import org.kar.archidata.model.UserByToken;
import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.SecurityContext;
import java.security.Principal; import java.security.Principal;
@ -12,8 +13,8 @@ class MySecurityContext implements SecurityContext {
private final GenericContext contextPrincipale; private final GenericContext contextPrincipale;
private final String sheme; private final String sheme;
public MySecurityContext(User user, String sheme) { public MySecurityContext(User user, UserByToken userByToken, String sheme) {
this.contextPrincipale = new GenericContext(user); this.contextPrincipale = new GenericContext(user, userByToken);
this.sheme = sheme; this.sheme = sheme;
} }
@ -24,24 +25,36 @@ class MySecurityContext implements SecurityContext {
@Override @Override
public boolean isUserInRole(String role) { public boolean isUserInRole(String role) {
if (role.contentEquals("ADMIN")) { if (contextPrincipale.user != null) {
return contextPrincipale.user.admin == true; if (role.contentEquals("ADMIN")) {
} return contextPrincipale.user.admin == true;
if (role.contentEquals("USER")) { }
// if not an admin, this is a user... if (role.contentEquals("USER")) {
return true; //contextPrincipale.user.admin == false; // if not an admin, this is a user...
return true; //contextPrincipale.user.admin == false;
}
} }
if (contextPrincipale.userByToken != null) {
Boolean value = this.contextPrincipale.userByToken.right.get(role);
return value == true;
}
return false; return false;
} }
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return true; return sheme.equalsIgnoreCase("https");
} }
@Override @Override
public String getAuthenticationScheme() { public String getAuthenticationScheme() {
return "Yota"; if (contextPrincipale.user != null) {
return "Yota";
}
if (contextPrincipale.userByToken != null) {
return "Zota";
}
return null;
} }
} }

View File

@ -26,12 +26,12 @@ public class GenericTable {
@SQLCreateTime @SQLCreateTime
@SQLNotNull @SQLNotNull
@SQLComment("Create time of the object") @SQLComment("Create time of the object")
@SQLDefault("now()") @SQLDefault("now(3)")
public Timestamp create_date = null; public Timestamp create_date = null;
@SQLNotRead @SQLNotRead
@SQLUpdateTime @SQLUpdateTime
@SQLNotNull @SQLNotNull
@SQLComment("When update the object") @SQLComment("When update the object")
@SQLDefault("now()") @SQLDefault("now(3)")
public Timestamp modify_date = null; public Timestamp modify_date = null;
} }

View File

@ -0,0 +1,23 @@
package org.kar.archidata.model;
import java.sql.Timestamp;
import org.kar.archidata.annotation.SQLIfNotExists;
import org.kar.archidata.annotation.SQLNotNull;
import org.kar.archidata.annotation.SQLTableName;
import com.fasterxml.jackson.annotation.JsonInclude;
@SQLTableName ("applicationToken")
@SQLIfNotExists
@JsonInclude(JsonInclude.Include.NON_NULL)
public class GenericToken extends GenericTable {
@SQLNotNull
public Long parentId;
@SQLNotNull
public String name;
@SQLNotNull
public Timestamp endValidityTime = null;
@SQLNotNull
public String token;
}

View File

@ -3,16 +3,7 @@ package org.kar.archidata.model;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
/*
CREATE TABLE `token` (
`id` bigint NOT NULL COMMENT 'Unique ID of the TOKEN' AUTO_INCREMENT PRIMARY KEY,
`userId` bigint NOT NULL COMMENT 'Unique ID of the user',
`token` varchar(128) COLLATE 'latin1_bin' NOT NULL COMMENT 'Token (can be not unique)',
`createTime` datetime NOT NULL COMMENT 'Time the token has been created',
`endValidityTime` datetime NOT NULL COMMENT 'Time of the token end validity'
) AUTO_INCREMENT=10;
*/
public class Token { public class Token {
public Long id; public Long id;
public Long userId; public Long userId;

View File

@ -0,0 +1,19 @@
package org.kar.archidata.model;
import java.util.HashMap;
import java.util.Map;
public class UserByToken {
public static final int TYPE_USER = -1;
public static final int TYPE_APPLICATION = -2;
// application internal management type: an application generic Id
public Integer type = null;
public Long id = null;
public Long parentId = null;
public String name = null;
// Right map
public Map<String, Boolean> right = new HashMap<>();
}

View File

@ -10,6 +10,7 @@ public class ConfigBaseVariable {
static public String bdDatabase = System.getenv("DB_DATABASE"); static public String bdDatabase = System.getenv("DB_DATABASE");
static public String apiAdress = System.getenv("API_ADDRESS"); static public String apiAdress = System.getenv("API_ADDRESS");
static public String ssoAdress = System.getenv("SSO_ADDRESS"); static public String ssoAdress = System.getenv("SSO_ADDRESS");
static public String ssoToken = System.getenv("SSO_TOKEN");
public static String getTmpDataFolder() { public static String getTmpDataFolder() {
if (tmpDataFolder == null) { if (tmpDataFolder == null) {
@ -74,4 +75,7 @@ public class ConfigBaseVariable {
} }
return ssoAdress; return ssoAdress;
} }
public static String ssoToken() {
return ssoToken;
}
} }

View File

@ -46,6 +46,10 @@ public class JWTWrapper {
con.setRequestProperty("Cache-Control", "no-cache"); con.setRequestProperty("Cache-Control", "no-cache");
con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept", "application/json"); con.setRequestProperty("Accept", "application/json");
String ssoToken = ConfigBaseVariable.ssoToken();
if (ssoToken != null) {
con.setRequestProperty("Authorization", "Zota " + ssoToken);
}
int responseCode = con.getResponseCode(); int responseCode = con.getResponseCode();
System.out.println("GET Response Code :: " + responseCode); System.out.println("GET Response Code :: " + responseCode);
@ -95,12 +99,19 @@ public class JWTWrapper {
} }
} }
public static String getPublicKey() { public static String getPublicKeyJson() {
if (rsaPublicJWK == null) { if (rsaPublicJWK == null) {
return null; return null;
} }
return rsaPublicJWK.toJSONString(); return rsaPublicJWK.toJSONString();
} }
public static java.security.interfaces.RSAPublicKey getPublicKeyJava() throws JOSEException {
if (rsaPublicJWK == null) {
return null;
}
// Convert back to std Java interface
return rsaPublicJWK.toRSAPublicKey();
}
/** /**
* Create a token with the provided elements * Create a token with the provided elements