[FEAT,API] (Token) upgrade tocken management to permit to control more conplex token

This commit is contained in:
Edouard DUPIN 2024-12-07 16:51:31 +01:00
parent dccb6b80d5
commit 091ac4babd
3 changed files with 118 additions and 15 deletions

View File

@ -12,6 +12,7 @@ import java.util.Map.Entry;
import org.kar.archidata.annotation.security.PermitTokenInURI; import org.kar.archidata.annotation.security.PermitTokenInURI;
import org.kar.archidata.catcher.RestErrorResponse; import org.kar.archidata.catcher.RestErrorResponse;
import org.kar.archidata.exception.SystemException;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import org.kar.archidata.tools.JWTWrapper; import org.kar.archidata.tools.JWTWrapper;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -23,6 +24,7 @@ import jakarta.annotation.Priority;
import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.DenyAll;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Priorities; import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.container.ContainerRequestFilter;
@ -42,18 +44,40 @@ public class AuthenticationFilter implements ContainerRequestFilter {
@Context @Context
private ResourceInfo resourceInfo; private ResourceInfo resourceInfo;
protected final String applicationName; protected final String applicationName;
protected final String issuer;
public static final String AUTHENTICATION_SCHEME = "Bearer"; public static final String AUTHENTICATION_SCHEME = "Bearer";
public static final String APIKEY = "ApiKey"; public static final String APIKEY = "ApiKey";
public AuthenticationFilter(final String applicationName) { public AuthenticationFilter(final String applicationName) {
this.applicationName = applicationName; this.applicationName = applicationName;
this.issuer = "KarAuth";
}
public AuthenticationFilter(final String applicationName, final String issuer) {
this.applicationName = applicationName;
this.issuer = issuer;
}
public String getRequestedPath(final ContainerRequestContext requestContext) {
final Class<?> resourceClass = this.resourceInfo.getResourceClass();
final Method resourceMethod = this.resourceInfo.getResourceMethod();
final String classPath = resourceClass.isAnnotationPresent(Path.class)
? resourceClass.getAnnotation(Path.class).value()
: "";
final String methodPath = resourceMethod.isAnnotationPresent(Path.class)
? resourceMethod.getAnnotation(Path.class).value()
: "";
final String fullPath = (classPath.startsWith("/") ? "" : "/") + classPath
+ (methodPath.startsWith("/") ? "" : "/") + methodPath;
return fullPath;
} }
@Override @Override
public void filter(final ContainerRequestContext requestContext) throws IOException { public void filter(final ContainerRequestContext requestContext) throws IOException {
/* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----"); /* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----");
* logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */ * logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */
final Method method = this.resourceInfo.getResourceMethod(); final Method method = this.resourceInfo.getResourceMethod();
// Access denied for all // Access denied for all
if (method.isAnnotationPresent(DenyAll.class)) { if (method.isAnnotationPresent(DenyAll.class)) {
@ -140,12 +164,13 @@ public class AuthenticationFilter implements ContainerRequestFilter {
final List<String> roles = Arrays.asList(rolesAnnotation.value()); final List<String> roles = Arrays.asList(rolesAnnotation.value());
// check if the user have the right: // check if the user have the right:
boolean haveRight = false; boolean haveRight = false;
for (final String role : roles) { try {
if (userContext.isUserInRole(role)) { haveRight = checkRight(requestContext, userContext, roles);
haveRight = true; } catch (final SystemException e) {
break; // TODO Auto-generated catch block
} e.printStackTrace();
} }
// Is user valid? // Is user valid?
if (!haveRight) { if (!haveRight) {
LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles); LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles);
@ -157,6 +182,18 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// logger.debug("Get local user : {} / {}", user, userByToken); // logger.debug("Get local user : {} / {}", user, userByToken);
} }
protected boolean checkRight(
final ContainerRequestContext requestContext,
final MySecurityContext userContext,
final List<String> roles) throws SystemException {
for (final String role : roles) {
if (userContext.isUserInRole(this.applicationName, role)) {
return true;
}
}
return false;
}
private boolean isTokenBasedAuthentication(final String authorizationHeader) { private boolean isTokenBasedAuthentication(final String authorizationHeader) {
// Check if the Authorization header is valid // Check if the Authorization header is valid
// It must not be null and must be prefixed with "Bearer" plus a whitespace // It must not be null and must be prefixed with "Bearer" plus a whitespace
@ -193,7 +230,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
// must be override to be good implementation // must be override to be good implementation
protected UserByToken validateJwtToken(final String authorization) throws Exception { protected UserByToken validateJwtToken(final String authorization) throws Exception {
// logger.debug(" validate token : " + authorization); // logger.debug(" validate token : " + authorization);
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null); final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, this.issuer, 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) {
LOGGER.error("The token is not valid: '{}'", authorization); LOGGER.error("The token is not valid: '{}'", authorization);
@ -208,13 +245,16 @@ public class AuthenticationFilter implements ContainerRequestFilter {
user.type = UserByToken.TYPE_USER; user.type = UserByToken.TYPE_USER;
final Object rowRight = ret.getClaim("right"); final Object rowRight = ret.getClaim("right");
if (rowRight != null) { if (rowRight != null) {
final Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right"); LOGGER.info("Detect right in Authentication Filer: {}", rowRight);
user.right = (Map<String, Map<String, Object>>) ret.getClaim("right");
/*
if (rights.containsKey(this.applicationName)) { if (rights.containsKey(this.applicationName)) {
user.right = rights.get(this.applicationName); user.right = rights.get(this.applicationName);
} else { } else {
LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName,
rights); rights);
} }
*/
} }
// logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight); // logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight);
return user; return user;

View File

@ -1,13 +1,17 @@
package org.kar.archidata.filter; package org.kar.archidata.filter;
import java.security.Principal; import java.security.Principal;
import java.util.Set;
import org.kar.archidata.model.UserByToken; import org.kar.archidata.model.UserByToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.ws.rs.core.SecurityContext; import jakarta.ws.rs.core.SecurityContext;
// https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/ // https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/
class MySecurityContext implements SecurityContext { public class MySecurityContext implements SecurityContext {
private static final Logger LOGGER = LoggerFactory.getLogger(MySecurityContext.class);
private final GenericContext contextPrincipale; private final GenericContext contextPrincipale;
private final String sheme; private final String sheme;
@ -22,10 +26,9 @@ class MySecurityContext implements SecurityContext {
return this.contextPrincipale; return this.contextPrincipale;
} }
@Override public boolean isUserInRole(final String group, final String role) {
public boolean isUserInRole(final String role) {
if (this.contextPrincipale.userByToken != null) { if (this.contextPrincipale.userByToken != null) {
final Object value = this.contextPrincipale.userByToken.right.get(role); final Object value = this.contextPrincipale.userByToken.getRight(group, role);
if (value instanceof final Boolean ret) { if (value instanceof final Boolean ret) {
return ret; return ret;
} }
@ -33,6 +36,43 @@ class MySecurityContext implements SecurityContext {
return false; return false;
} }
public Object getUserInRole(final String group, final String role) {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.getRight(group, role);
}
return null;
}
public Set<String> getGroups() {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.getGroups();
}
return Set.of();
}
public boolean groupExist(final String group) {
if (this.contextPrincipale.userByToken != null) {
return this.contextPrincipale.userByToken.groupExist(group);
}
return false;
}
@Override
public boolean isUserInRole(final String role) {
// TODO Auto-generated method stub
return isUserInRole("???", role);
}
public Object getRole(final String role) {
LOGGER.info("contextPrincipale={}", this.contextPrincipale);
if (this.contextPrincipale.userByToken != null) {
LOGGER.info("contextPrincipale.userByToken={}", this.contextPrincipale.userByToken);
LOGGER.info("contextPrincipale.userByToken.right={}", this.contextPrincipale.userByToken.right);
return this.contextPrincipale.userByToken.right.get(role);
}
return null;
}
@Override @Override
public boolean isSecure() { public boolean isSecure() {
return "https".equalsIgnoreCase(this.sheme); return "https".equalsIgnoreCase(this.sheme);

View File

@ -2,6 +2,7 @@ package org.kar.archidata.model;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
public class UserByToken { public class UserByToken {
public static final int TYPE_USER = -1; public static final int TYPE_USER = -1;
@ -13,13 +14,35 @@ public class UserByToken {
public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID 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, Object> right = new HashMap<>(); public Map<String, Map<String, Object>> right = new HashMap<>();
public boolean hasRight(final String key, final Object value) { public Set<String> getGroups() {
if (!this.right.containsKey(key)) { return this.right.keySet();
}
public boolean groupExist(final String group) {
if (!this.right.containsKey(group)) {
return false;
}
return this.right.containsKey(group);
}
public Object getRight(final String group, final String key) {
if (!this.right.containsKey(group)) {
return null;
}
final Map<String, Object> rightGroup = this.right.get(group);
if (!rightGroup.containsKey(key)) {
return null;
}
return rightGroup.get(key);
}
public boolean hasRight(final String group, final String key, final Object value) {
final Object data = getRight(group, key);
if (data == null) {
return false; return false;
} }
final Object data = this.right.get(key);
if (data instanceof final Boolean elem) { if (data instanceof final Boolean elem) {
if (value instanceof final Boolean castVal) { if (value instanceof final Boolean castVal) {
if (elem.equals(castVal)) { if (elem.equals(castVal)) {