[DEV] rework authentication

This commit is contained in:
Edouard DUPIN 2023-04-22 00:32:29 +02:00
parent 12231762d3
commit dc8cae5150
10 changed files with 75 additions and 75 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.3.4</version> <version>0.3.5</version>
<properties> <properties>
<jersey.version>3.1.1</jersey.version> <jersey.version>3.1.1</jersey.version>
<jaxb.version>2.3.1</jaxb.version> <jaxb.version>2.3.1</jaxb.version>

View File

@ -1,6 +1,4 @@
package org.kar.archidata; package org.kar.archidata;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
public class MigrationSystem { public class MigrationSystem {

View File

@ -283,7 +283,7 @@ public class SqlWrapper {
// real add in the BDD: // real add in the BDD:
try { try {
String tableName = getTableName(clazz); String tableName = getTableName(clazz);
boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0; //boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
StringBuilder query = new StringBuilder(); StringBuilder query = new StringBuilder();
query.append("INSERT INTO "); query.append("INSERT INTO ");
query.append(tableName); query.append(tableName);
@ -455,7 +455,7 @@ public class SqlWrapper {
// real add in the BDD: // real add in the BDD:
try { try {
String tableName = getTableName(clazz); String tableName = getTableName(clazz);
boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0; //boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
StringBuilder query = new StringBuilder(); StringBuilder query = new StringBuilder();
query.append("UPDATE "); query.append("UPDATE ");
query.append(tableName); query.append(tableName);
@ -463,7 +463,6 @@ public class SqlWrapper {
boolean firstField = true; boolean firstField = true;
Field primaryKeyField = null; Field primaryKeyField = null;
int count = 0;
for (Field elem : clazz.getFields()) { for (Field elem : clazz.getFields()) {
boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0; boolean primaryKey = elem.getDeclaredAnnotationsByType(SQLPrimaryKey.class).length != 0;
if (primaryKey) { if (primaryKey) {
@ -489,7 +488,6 @@ public class SqlWrapper {
continue; continue;
} }
} }
count++;
if (firstField) { if (firstField) {
firstField = false; firstField = false;
} else { } else {
@ -551,7 +549,6 @@ public class SqlWrapper {
ps.setString(iii++, dataTmp); ps.setString(iii++, dataTmp);
} }
} }
count++;
} }
ps.setLong(iii++, id); ps.setLong(iii++, id);
// execute the request // execute the request
@ -693,6 +690,7 @@ public class SqlWrapper {
stmt.executeUpdate(querry); stmt.executeUpdate(querry);
} }
@SuppressWarnings("unchecked")
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<>();
@ -774,7 +772,7 @@ public class SqlWrapper {
if (!full && createTime) { if (!full && createTime) {
continue; continue;
} }
String name = elem.getName(); //String name = elem.getName();
boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0; boolean updateTime = elem.getDeclaredAnnotationsByType(SQLUpdateTime.class).length != 0;
if (!full && updateTime) { if (!full && updateTime) {
continue; continue;
@ -806,7 +804,7 @@ public class SqlWrapper {
} }
} }
if (primaryKeyField != null) { if (primaryKeyField != null) {
return getWhere(clazz, primaryKeyField.getName(), "=", id); return SqlWrapper.getWhere(clazz, List.of(new WhereCondition(primaryKeyField.getName(), "=", id)), false);
} }
throw new Exception("Missing primary Key..."); throw new Exception("Missing primary Key...");
} }
@ -828,7 +826,7 @@ public class SqlWrapper {
// real add in the BDD: // real add in the BDD:
try { try {
String tableName = getTableName(clazz); String tableName = getTableName(clazz);
boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0; //boolean createIfNotExist = clazz.getDeclaredAnnotationsByType(SQLIfNotExists.class).length != 0;
StringBuilder query = new StringBuilder(); StringBuilder query = new StringBuilder();
query.append("SELECT "); query.append("SELECT ");
//query.append(tableName); //query.append(tableName);
@ -985,7 +983,7 @@ public class SqlWrapper {
if (affectedRows == 0) { if (affectedRows == 0) {
throw new SQLException("Creating data failed, no rows affected."); throw new SQLException("Creating data failed, no rows affected.");
} }
// retreive uid inserted // retrieve uid inserted
try (ResultSet generatedKeys = ps.getGeneratedKeys()) { try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
if (generatedKeys.next()) { if (generatedKeys.next()) {
uniqueSQLID = generatedKeys.getLong(1); uniqueSQLID = generatedKeys.getLong(1);
@ -1045,7 +1043,7 @@ public class SqlWrapper {
return out; return out;
} }
/** /**
* Convert the list if external Ids in a string '-' separated * Convert the list if external id in a string '-' separated
* @param ids List of value (null are removed) * @param ids List of value (null are removed)
* @return '-' string separated * @return '-' string separated
*/ */

View File

@ -229,7 +229,7 @@ public class DataResource {
public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) { public Response uploadFile(@Context SecurityContext sc, @FormDataParam("file") InputStream fileInputStream, @FormDataParam("file") FormDataContentDisposition fileMetaData) {
GenericContext gc = (GenericContext) sc.getUserPrincipal(); GenericContext gc = (GenericContext) sc.getUserPrincipal();
System.out.println("==================================================="); System.out.println("===================================================");
System.out.println("== DATA uploadFile " + (gc==null?"null":gc.user)); System.out.println("== DATA uploadFile " + (gc==null?"null":gc.userByToken));
System.out.println("==================================================="); System.out.println("===================================================");
//public NodeSmall uploadFile(final FormDataMultiPart form) { //public NodeSmall uploadFile(final FormDataMultiPart form) {
System.out.println("Upload file: "); System.out.println("Upload file: ");
@ -252,7 +252,7 @@ public class DataResource {
public Response retriveDataId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception { public Response retriveDataId(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal(); GenericContext gc = (GenericContext) sc.getUserPrincipal();
//System.out.println("==================================================="); //System.out.println("===================================================");
System.out.println("== DATA retriveDataId ? id=" + id + " user=" + (gc==null?"null":gc.user)); System.out.println("== DATA retriveDataId ? id=" + id + " user=" + (gc==null?"null":gc.userByToken));
//System.out.println("==================================================="); //System.out.println("===================================================");
Data value = getSmall(id); Data value = getSmall(id);
if (value == null) { if (value == null) {
@ -344,7 +344,7 @@ public class DataResource {
public Response retriveDataFull(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id, @PathParam("name") String name) throws Exception { public Response retriveDataFull(@Context SecurityContext sc, @QueryParam(HttpHeaders.AUTHORIZATION) String token, @HeaderParam("Range") String range, @PathParam("id") Long id, @PathParam("name") String name) throws Exception {
GenericContext gc = (GenericContext) sc.getUserPrincipal(); GenericContext gc = (GenericContext) sc.getUserPrincipal();
//System.out.println("==================================================="); //System.out.println("===================================================");
System.out.println("== DATA retriveDataFull ? id=" + id + " user=" + (gc==null?"null":gc.user)); System.out.println("== DATA retriveDataFull ? id=" + id + " user=" + (gc==null?"null":gc.userByToken));
//System.out.println("==================================================="); //System.out.println("===================================================");
Data value = getSmall(id); Data value = getSmall(id);
if (value == null) { if (value == null) {

View File

@ -28,6 +28,7 @@ import com.nimbusds.jwt.JWTClaimsSet;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey // https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey
// https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178 // https://stackoverflow.com/questions/26777083/best-practice-for-rest-token-based-authentication-with-jax-rs-and-jersey/45814178#45814178
@ -39,10 +40,18 @@ import java.util.Map.Entry;
public class AuthenticationFilter implements ContainerRequestFilter { public class AuthenticationFilter implements ContainerRequestFilter {
@Context @Context
private ResourceInfo resourceInfo; private ResourceInfo resourceInfo;
protected final String applicationName;
private static final String AUTHENTICATION_SCHEME = "Yota"; private static final String AUTHENTICATION_SCHEME = "Yota";
private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota"; private static final String AUTHENTICATION_TOKEN_SCHEME = "Zota";
public AuthenticationFilter(String applicationName) {
super();
this.applicationName = applicationName;
}
@Override @Override
public void filter(ContainerRequestContext requestContext) throws IOException { public void filter(ContainerRequestContext requestContext) throws IOException {
/* /*
@ -95,38 +104,23 @@ public class AuthenticationFilter implements ContainerRequestFilter {
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return; return;
} }
User user = null;
UserByToken userByToken = null; UserByToken userByToken = null;
if (isJwtToken) { if (isJwtToken) {
// Extract the token from the Authorization header (Remove "Yota ") // Extract the token from the Authorization header (Remove "Yota ")
String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim(); String token = authorizationHeader.substring(AUTHENTICATION_SCHEME.length()).trim();
//System.out.println("token: " + token); //System.out.println("token: " + token);
try { try {
user = validateJwtToken(token); userByToken = validateJwtToken(token);
} catch (Exception e) { } catch (Exception e) {
System.out.println("Fail to validate token: " + e.getMessage()); System.out.println("Fail to validate token: " + e.getMessage());
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return; return;
} }
if (user == null) { if (userByToken == null) {
System.out.println("get a NULL user ..."); System.out.println("get a NULL user ...");
abortWithUnauthorized(requestContext); abortWithUnauthorized(requestContext);
return; 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 { } else {
// Extract the token from the Authorization header (Remove "Zota ") // Extract the token from the Authorization header (Remove "Zota ")
String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim(); String token = authorizationHeader.substring(AUTHENTICATION_TOKEN_SCHEME.length()).trim();
@ -147,7 +141,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
// 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, userByToken, scheme); MySecurityContext userContext = new MySecurityContext(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());
@ -196,11 +190,11 @@ public class AuthenticationFilter implements ContainerRequestFilter {
} }
protected UserByToken validateToken(String authorization) throws Exception { protected UserByToken validateToken(String authorization) throws Exception {
System.out.println("dsfgsdgsdfgsdgf"); System.out.println("Must be Override by the application implmentation, otherwise it dose not work");
return null; return null;
} }
// must be override to be good implemenres // must be override to be good implementation
protected User validateJwtToken(String authorization) throws Exception { protected UserByToken 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...
@ -211,7 +205,21 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// check userID // check userID
String userUID = ret.getSubject(); String userUID = ret.getSubject();
long id = Long.parseLong(userUID); long id = Long.parseLong(userUID);
System.out.println("request user: '" + userUID + "'"); UserByToken user = new UserByToken();
return UserDB.getUserOrCreate(id, (String)ret.getClaim("login") ); user.id = id;
user.name = (String)ret.getClaim("login");
user.type = UserByToken.TYPE_USER;
Object rowRight = ret.getClaim("right");
if (rowRight != null) {
Map<String, Map<String,Object>> rights = (Map<String, Map<String,Object>>) ret.getClaim("right");
if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName);
} else {
System.out.println("Connect with no right for this application '" + this.applicationName + "' full Right='" + rights + "'");
}
}
System.out.println("request user: '" + userUID + "' right: '" + user.right + "' row='" +rowRight + "'");
return user;
//return UserDB.getUserOrCreate(id, (String)ret.getClaim("login") );
} }
} }

View File

@ -1,25 +1,22 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import org.kar.archidata.model.User;
import org.kar.archidata.model.UserByToken; 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 UserByToken userByToken; public UserByToken userByToken;
public GenericContext(User user, UserByToken userByToken) { public GenericContext(UserByToken userByToken) {
this.user = user;
this.userByToken = userByToken; this.userByToken = userByToken;
} }
@Override @Override
public String getName() { public String getName() {
if (user == null) { if (this.userByToken == null) {
return "???"; return "???";
} }
return user.login; return this.userByToken.name;
} }
} }

View File

@ -1,7 +1,5 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import org.kar.archidata.model.User;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
@ -13,8 +11,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, UserByToken userByToken, String sheme) { public MySecurityContext(UserByToken userByToken, String sheme) {
this.contextPrincipale = new GenericContext(user, userByToken); this.contextPrincipale = new GenericContext(userByToken);
this.sheme = sheme; this.sheme = sheme;
} }
@ -25,18 +23,11 @@ class MySecurityContext implements SecurityContext {
@Override @Override
public boolean isUserInRole(String role) { public boolean isUserInRole(String role) {
if (contextPrincipale.user != null) {
if (role.contentEquals("ADMIN")) {
return contextPrincipale.user.admin == true;
}
if (role.contentEquals("USER")) {
// if not an admin, this is a user...
return true; //contextPrincipale.user.admin == false;
}
}
if (contextPrincipale.userByToken != null) { if (contextPrincipale.userByToken != null) {
Boolean value = this.contextPrincipale.userByToken.right.get(role); Object value = this.contextPrincipale.userByToken.right.get(role);
return value == true; if (value instanceof Boolean ret) {
return ret;
}
} }
return false; return false;
} }
@ -48,9 +39,6 @@ class MySecurityContext implements SecurityContext {
@Override @Override
public String getAuthenticationScheme() { public String getAuthenticationScheme() {
if (contextPrincipale.user != null) {
return "Yota";
}
if (contextPrincipale.userByToken != null) { if (contextPrincipale.userByToken != null) {
return "Zota"; return "Zota";
} }

View File

@ -15,11 +15,14 @@ CREATE TABLE `user` (
*/ */
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.List;
import org.kar.archidata.annotation.SQLComment;
import org.kar.archidata.annotation.SQLDefault; import org.kar.archidata.annotation.SQLDefault;
import org.kar.archidata.annotation.SQLIfNotExists; import org.kar.archidata.annotation.SQLIfNotExists;
import org.kar.archidata.annotation.SQLLimitSize; import org.kar.archidata.annotation.SQLLimitSize;
import org.kar.archidata.annotation.SQLNotNull; import org.kar.archidata.annotation.SQLNotNull;
import org.kar.archidata.annotation.SQLTableLinkGeneric;
import org.kar.archidata.annotation.SQLTableName; import org.kar.archidata.annotation.SQLTableName;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;

View File

@ -11,9 +11,9 @@ public class UserByToken {
public Integer type = null; public Integer type = null;
public Long id = null; public Long id = null;
public Long parentId = null; public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID
public String name = null; public String name = null;
// Right map // Right map
public Map<String, Boolean> right = new HashMap<>(); public Map<String, Object> right = new HashMap<>();
} }

View File

@ -7,6 +7,8 @@ import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -72,11 +74,14 @@ public class JWTWrapper {
System.out.println("GET JWT validator token not worked"); System.out.println("GET JWT validator token not worked");
} }
public static void initLocalToken() throws Exception{ public static void initLocalToken(String baseUUID) throws Exception{
// RSA signatures require a public and private RSA key pair, the public key // RSA signatures require a public and private RSA key pair, the public key
// must be made known to the JWS recipient in order to verify the signatures // must be made known to the JWS recipient in order to verify the signatures
try { try {
String generatedStringForKey = UUID.randomUUID().toString(); String generatedStringForKey = baseUUID;
if (generatedStringForKey == null) {
generatedStringForKey = UUID.randomUUID().toString();
}
rsaJWK = new RSAKeyGenerator(2048).keyID(generatedStringForKey).generate(); rsaJWK = new RSAKeyGenerator(2048).keyID(generatedStringForKey).generate();
rsaPublicJWK = rsaJWK.toPublicJWK(); rsaPublicJWK = rsaJWK.toPublicJWK();
//System.out.println("RSA key (all): " + rsaJWK.toJSONString()); //System.out.println("RSA key (all): " + rsaJWK.toJSONString());
@ -121,7 +126,7 @@ 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, String application, int timeOutInMunites) { public static String generateJWToken(long userID, String userLogin, String isuer, String application, Map<String, Object> rights, 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;
@ -135,16 +140,19 @@ public class JWTWrapper {
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);
// Prepare JWT with claims set JWTClaimsSet.Builder builder = 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) .claim("application", application)
.issuer(isuer) .issuer(isuer)
.issueTime(new Date()) .issueTime(new Date())
.expirationTime(new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */)) // Do not ask why we need a "-" here ... this have no meaning .expirationTime(new Date(new Date().getTime() - 60 * timeOutInMunites * 1000 /* millisecond */)); // Do not ask why we need a "-" here ... this have no meaning
.build(); // add right if needed:
if (rights != null && !rights.isEmpty()) {
builder.claim("right", rights);
}
// Prepare JWT with claims set
JWTClaimsSet claimsSet = builder.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);
// Compute the RSA signature // Compute the RSA signature