diff --git a/compile-libs/tomcat-annotations-api-10.0.0-M5.jar b/compile-libs/tomcat-annotations-api-10.0.0-M5.jar new file mode 100644 index 0000000000000000000000000000000000000000..b997f7299fd0181569a13d688530eb42661e754f Binary files /dev/null and b/compile-libs/tomcat-annotations-api-10.0.0-M5.jar differ diff --git a/compile-libs/tomcat-embed-core-10.0.0-M5.jar b/compile-libs/tomcat-embed-core-10.0.0-M5.jar new file mode 100644 index 0000000000000000000000000000000000000000..2f1d6c7ee4363654e453428ce5efbc685db8c94e Binary files /dev/null and b/compile-libs/tomcat-embed-core-10.0.0-M5.jar differ diff --git a/jams-common/pom.xml b/jams-common/pom.xml index 42b1c36f288b9057d6983effed588fd16cb722ab..0976ef9e5f921ad310f6c531353c7943903b57f0 100644 --- a/jams-common/pom.xml +++ b/jams-common/pom.xml @@ -39,4 +39,5 @@ </dependency> </dependencies> + </project> \ No newline at end of file diff --git a/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletAnnotationScanner.java b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletAnnotationScanner.java index 5d1d965bd93f6c42fc45a45a6201d35537d42a87..e09eb2b63755f5b0b14b795b7bec93d12189abd8 100644 --- a/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletAnnotationScanner.java +++ b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletAnnotationScanner.java @@ -65,7 +65,6 @@ public class ScopedServletAnnotationScanner { if(classChanged){ try { if (cc.isFrozen()) cc.defrost(); - cc.writeFile("."); cc.toClass(); log.info("Successfully modified class " + classname); } diff --git a/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletMethod.java b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletMethod.java index 028eed32f3c85210088b0b315c8ffe951cbe88c8..9ece2a6a4985b3e45ab5736f7830d9f3bef24ef3 100644 --- a/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletMethod.java +++ b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletMethod.java @@ -8,7 +8,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) +@Retention(RetentionPolicy.SOURCE) public @interface ScopedServletMethod { public AccessLevel[] securityGroups(); } diff --git a/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletProcessor.java b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..a0d81bdeb5f4cc7b7472948612bb6d19e22dac37 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/annotations/ScopedServletProcessor.java @@ -0,0 +1,122 @@ +package net.jami.jams.common.annotations; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.LoaderClassPath; +import net.jami.jams.common.objects.user.AccessLevel; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic; +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashMap; +import java.util.Set; + +@SupportedAnnotationTypes("net.jami.jams.common.annotations.ScopedServletMethod") +@SupportedSourceVersion(SourceVersion.RELEASE_11) +@SupportedOptions({"moduleDir","parentDir"}) +//This is a bit of hack to modify already compiled sources at compilation time +public class ScopedServletProcessor extends AbstractProcessor { + + private String moduleDirectory = null; + private String parentDirectory = null; + + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + //Get the current project working directory: + moduleDirectory = processingEnv.getOptions().get("moduleDir"); + parentDirectory = processingEnv.getOptions().get("parentDir"); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Project Directory: " + moduleDirectory); + if (roundEnv.processingOver()) { + return false; + } + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING,"Working in directory: " + System.getProperty("user.dir")); + Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(ScopedServletMethod.class); + //Inside each annotation, we have a list of stuff. + HashMap<String, HashMap<String,AccessLevel[]>> annList = new HashMap<>(); + annotatedElements.forEach(element -> { + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Found class: " + element.getEnclosingElement().toString()); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Found method: " + element.toString()); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING,"Found " + element.getAnnotation(ScopedServletMethod.class).securityGroups().length + " groups"); + String clsName = element.getEnclosingElement().toString(); + String mthName = element.toString(); + AccessLevel[] levels = element.getAnnotation(ScopedServletMethod.class).securityGroups(); + annList.putIfAbsent(clsName,new HashMap<>()); + annList.get(clsName).putIfAbsent(mthName,levels); + }); + annList.forEach(this::processClass); + + return true; + } + + private void processClass(String classname,HashMap<String,AccessLevel[]> methodNames) { + try { + + URL cpUrl1 = new URL("jar:file:" + parentDirectory + File.separator + "compile-libs/tomcat-embed-core-10.0.0-M5.jar!/"); + URL cpUrl3 = new URL("jar:file:" + parentDirectory + File.separator + "compile-libs/tomcat-annotations-api-10.0.0-M5.jar!/"); + + URLClassLoader cpUrlLoader = new URLClassLoader(new URL[]{cpUrl1,cpUrl3}); + + StringBuilder path = new StringBuilder(); + path.append(moduleDirectory); + path.append(File.separator); + path.append("target"); + path.append(File.separator); + path.append("classes"); + path.append(File.separator); + path.append(classname.replaceAll("\\.",File.separator)); + path.append(".class"); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING,"Now processing: " + path.toString()); + ClassPool pool = ClassPool.getDefault(); + //Load tomcat & commons classpath.. + pool.insertClassPath(new LoaderClassPath(cpUrlLoader)); + CtClass aclClass = pool.makeClass(new FileInputStream(parentDirectory+"/jams-common/target/classes/net/jami/jams/common/objects/user/AccessLevel.class")); + CtClass ctClass = pool.makeClass(new FileInputStream(path.toString())); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING,ctClass.getName()); + //Now that we have the CtClass we copy past + for(String rawmethodName : methodNames.keySet()) { + String methodName = rawmethodName.split("\\(")[0]; + for (int i = 0; i < ctClass.getMethods().length; i++) { + CtMethod method = ctClass.getMethods()[i]; + if (method.getName().equals(methodName)) { + //Insert code. + StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append("boolean allowed = false;\n"); + sb.append("net.jami.jams.common.objects.user.AccessLevel level = (net.jami.jams.common.objects.user.AccessLevel) req.getAttribute(\"accessLevel\");\n"); + for(int j=0; j<methodNames.get(rawmethodName).length; j++) { + sb.append("if(level == net.jami.jams.common.objects.user.AccessLevel.valueOf(\"").append(methodNames.get(rawmethodName)[j].toString()).append("\")) allowed = true;\n"); + } + sb.append("if(!allowed){\n"); + sb.append("resp.sendError(403,\"No valid access level found!\");\n"); + sb.append("return;\n"); + sb.append("}\n"); + sb.append("}\n"); + ctClass.getMethods()[i].insertBefore(sb.toString()); + //Persist the class. + } + } + } + path = new StringBuilder(); + path.append(moduleDirectory); + path.append(File.separator); + path.append("target"); + path.append(File.separator); + path.append("generated-sources"); + ctClass.writeFile(path.toString()); + processingEnv.getMessager().printMessage(Diagnostic.Kind.MANDATORY_WARNING, "Saved the class..."); + } catch (Exception e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,e.getMessage()); + } + } + //The only way to modify a file at this point would be to read it, and do a LOT of regex. +} diff --git a/jams-server/pom.xml b/jams-server/pom.xml index 258eaeccda6cd161d151a9dea2a4a33991d02217..60df9095de5e8b08cf9703f139d1b7f384e3331e 100644 --- a/jams-server/pom.xml +++ b/jams-server/pom.xml @@ -61,22 +61,70 @@ <artifactId>asm</artifactId> <version>${asm.version}</version> </dependency> - <dependency> - <groupId>net.jami</groupId> - <artifactId>datastore</artifactId> - <version>2.0</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>net.jami</groupId> - <artifactId>jami-nameserver</artifactId> - <version>2.0</version> - <scope>compile</scope> - </dependency> </dependencies> <build> <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.version}</version> + <executions> + <execution> + <id>Compile With Annotation Processing</id> + <phase>compile</phase> + <goals> + <goal>compile</goal> + </goals> + <configuration> + <source>11</source> + <target>11</target> + <annotationProcessorPaths> + <path> + <groupId>net.jami</groupId> + <artifactId>jams-common</artifactId> + <version>${revision}</version> + </path> + </annotationProcessorPaths> + <annotationProcessors> + <annotationProcessor> + lombok.launch.AnnotationProcessorHider$AnnotationProcessor + </annotationProcessor> + <annotationProcessor> + net.jami.jams.common.annotations.ScopedServletProcessor + </annotationProcessor> + </annotationProcessors> + <compilerArgs> + <arg>-AmoduleDir=${pom.basedir}</arg> + <arg>-AparentDir=${project.parent.basedir}</arg> + </compilerArgs> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-resources-plugin</artifactId> + <version>3.1.0</version> + <executions> + <execution> + <id>copy-resource-one</id> + <phase>compile</phase> + <goals> + <goal>copy-resources</goal> + </goals> + <configuration> + <outputDirectory>${pom.basedir}/target/classes</outputDirectory> + <resources> + <resource> + <directory>${pom.basedir}/target/generated-sources/</directory> + <filtering>false</filtering> + </resource> + </resources> + <overwrite>true</overwrite> + </configuration> + </execution> + </executions> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java index d7f3701e31df77046ff96e7bda0a5f098305a02f..51991a3420f8a82f8e7ce9974e11b59a4c33da4b 100644 --- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java @@ -92,7 +92,7 @@ public class UserServlet extends HttpServlet { * @apiError (500) {null} null could not changed password */ @Override - @ScopedServletMethod(securityGroups = {AccessLevel.USER}) + @ScopedServletMethod(securityGroups = AccessLevel.USER) protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String username = req.getAttribute("username").toString(); //Check if he is AD/LDAP - then return a 401, because we can't set such password.