diff --git a/src/org/kar/archidata/filter/AuthenticationFilter.java b/src/org/kar/archidata/filter/AuthenticationFilter.java index d0b0e6d..efcd106 100644 --- a/src/org/kar/archidata/filter/AuthenticationFilter.java +++ b/src/org/kar/archidata/filter/AuthenticationFilter.java @@ -12,6 +12,7 @@ import java.util.Map.Entry; import org.kar.archidata.annotation.security.PermitTokenInURI; import org.kar.archidata.catcher.RestErrorResponse; +import org.kar.archidata.exception.SystemException; import org.kar.archidata.model.UserByToken; import org.kar.archidata.tools.JWTWrapper; import org.slf4j.Logger; @@ -23,6 +24,7 @@ import jakarta.annotation.Priority; import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.RolesAllowed; +import jakarta.ws.rs.Path; import jakarta.ws.rs.Priorities; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.container.ContainerRequestFilter; @@ -42,18 +44,40 @@ public class AuthenticationFilter implements ContainerRequestFilter { @Context private ResourceInfo resourceInfo; protected final String applicationName; + protected final String issuer; public static final String AUTHENTICATION_SCHEME = "Bearer"; public static final String APIKEY = "ApiKey"; public AuthenticationFilter(final String 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 public void filter(final ContainerRequestContext requestContext) throws IOException { /* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----"); * logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */ + final Method method = this.resourceInfo.getResourceMethod(); // Access denied for all if (method.isAnnotationPresent(DenyAll.class)) { @@ -140,12 +164,13 @@ public class AuthenticationFilter implements ContainerRequestFilter { final List roles = Arrays.asList(rolesAnnotation.value()); // check if the user have the right: boolean haveRight = false; - for (final String role : roles) { - if (userContext.isUserInRole(role)) { - haveRight = true; - break; - } + try { + haveRight = checkRight(requestContext, userContext, roles); + } catch (final SystemException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } + // Is user valid? if (!haveRight) { 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); } + protected boolean checkRight( + final ContainerRequestContext requestContext, + final MySecurityContext userContext, + final List roles) throws SystemException { + for (final String role : roles) { + if (userContext.isUserInRole(this.applicationName, role)) { + return true; + } + } + return false; + } + private boolean isTokenBasedAuthentication(final String authorizationHeader) { // Check if the Authorization header is valid // 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 protected UserByToken validateJwtToken(final String authorization) throws Exception { // 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... if (ret == null) { LOGGER.error("The token is not valid: '{}'", authorization); @@ -208,13 +245,16 @@ public class AuthenticationFilter implements ContainerRequestFilter { user.type = UserByToken.TYPE_USER; final Object rowRight = ret.getClaim("right"); if (rowRight != null) { - final Map> rights = (Map>) ret.getClaim("right"); + LOGGER.info("Detect right in Authentication Filer: {}", rowRight); + user.right = (Map>) ret.getClaim("right"); + /* if (rights.containsKey(this.applicationName)) { user.right = rights.get(this.applicationName); } else { LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName, rights); } + */ } // logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight); return user; diff --git a/src/org/kar/archidata/filter/MySecurityContext.java b/src/org/kar/archidata/filter/MySecurityContext.java index af2b386..630b1a0 100644 --- a/src/org/kar/archidata/filter/MySecurityContext.java +++ b/src/org/kar/archidata/filter/MySecurityContext.java @@ -1,13 +1,17 @@ package org.kar.archidata.filter; import java.security.Principal; +import java.util.Set; import org.kar.archidata.model.UserByToken; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import jakarta.ws.rs.core.SecurityContext; // 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 String sheme; @@ -22,10 +26,9 @@ class MySecurityContext implements SecurityContext { return this.contextPrincipale; } - @Override - public boolean isUserInRole(final String role) { + public boolean isUserInRole(final String group, final String role) { 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) { return ret; } @@ -33,6 +36,43 @@ class MySecurityContext implements SecurityContext { 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 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 public boolean isSecure() { return "https".equalsIgnoreCase(this.sheme); diff --git a/src/org/kar/archidata/model/UserByToken.java b/src/org/kar/archidata/model/UserByToken.java index 54a07fb..560d2db 100644 --- a/src/org/kar/archidata/model/UserByToken.java +++ b/src/org/kar/archidata/model/UserByToken.java @@ -2,6 +2,7 @@ package org.kar.archidata.model; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class UserByToken { 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 String name = null; // Right map - public Map right = new HashMap<>(); + public Map> right = new HashMap<>(); - public boolean hasRight(final String key, final Object value) { - if (!this.right.containsKey(key)) { + public Set getGroups() { + 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 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; } - final Object data = this.right.get(key); if (data instanceof final Boolean elem) { if (value instanceof final Boolean castVal) { if (elem.equals(castVal)) {