Skip to content
Snippets Groups Projects
Commit 58ff7f36 authored by William Enright's avatar William Enright
Browse files

Fixed some endpoints having no JSON content type in header

Change-Id: I7e5c32460df834f8badb73f9f41d54a95a0c87dd
parent 35413a7c
No related branches found
No related tags found
No related merge requests found
Showing
with 80 additions and 59 deletions
package net.jami.jams.common.annotations;
public @interface JsonContent {
}
......@@ -20,19 +20,20 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.jami.jams.server.startup;
package net.jami.jams.common.annotations;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class PackageScanner {
public static HashSet<String> getClasses() throws Exception {
HashSet<String> classNames = new HashSet<>();
ZipInputStream zip = new ZipInputStream(new FileInputStream(System.getProperty("user.dir") + File.separator + "jams-server.jar"));
public static ArrayList<String> getClasses(String jarFile) throws Exception {
ArrayList<String> classNames = new ArrayList<>();
ZipInputStream zip = new ZipInputStream(new FileInputStream(System.getProperty("user.dir") + File.separator + jarFile));
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
// This ZipEntry represents a class. Now, what class does it represent?
......
......@@ -25,78 +25,79 @@ package net.jami.jams.common.annotations;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.AttributeInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import lombok.extern.slf4j.Slf4j;
import net.jami.jams.common.objects.user.AccessLevel;
import net.jami.jams.common.updater.FileDescription;
import org.bouncycastle.util.encoders.Hex;
import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@Slf4j
@Deprecated(since = "2.0", forRemoval = true)
public class ScopedServletAnnotationScanner {
public void processClasses(String jarFile){
try {
ArrayList<String> classNames = PackageScanner.getClasses(jarFile);
classNames.parallelStream().forEach(this::processClass);
public void scanAndModify(HashSet<String> classes){
classes.parallelStream().forEach(this::processClass);
} catch (Exception e) {
log.info("Could not modify a target class with error {}",e.getMessage());
}
}
public void processClass(String classname){
public void processClass(String className) {
try {
CtClass cc = ClassPool.getDefault().get(classname);
className = className.replace("/", ".").replace(".class", "");
CtClass cc = ClassPool.getDefault().get(className);
cc.defrost();
CtMethod[] ctMethods = cc.getMethods();
boolean classChanged = false;
for (int i = 0; i < ctMethods.length; i++) {
MethodInfo minfo = ctMethods[i].getMethodInfo();
if(minfo.getAttribute(AnnotationsAttribute.visibleTag) == null){
continue;
}
AnnotationsAttribute attr = (AnnotationsAttribute) minfo.getAttribute(AnnotationsAttribute.visibleTag);
Annotation[] an = attr.getAnnotations();
for(int j=0; j<an.length; j++) {
if(an[j].getTypeName().equals(ScopedServletMethod.class.getName())) {
ArrayMemberValue memberValue = (ArrayMemberValue) an[j].getMemberValue("securityGroups");
HashSet<AccessLevel> accessLevels = new HashSet<>();
for(int k =0; k<memberValue.getValue().length; k++){
EnumMemberValue level = (EnumMemberValue) memberValue.getValue()[k];
AccessLevel accessLevel = AccessLevel.valueOf(level.getValue());
classChanged = true;
accessLevels.add(accessLevel);
for(AttributeInfo ai : minfo.getAttributes()) {
if (ai.getClass().getName().contains("AnnotationsAttribute")) {
AnnotationsAttribute aa = (AnnotationsAttribute) ai;
for (Annotation a : aa.getAnnotations()) {
if (a.getTypeName().equals(JsonContent.class.getName())) {
log.info("[{}] has secured method {}, modifying method... ", cc.getSimpleName(), ctMethods[i].getName());
//Build the code block that enforces security.
StringBuilder sb = new StringBuilder();
sb.append("{\n");
//So this does not play nice when trying to use hash sets...
sb.append("resp.setContentType(\"application/json\");\n");
sb.append("}\n");
ctMethods[i].insertBefore(sb.toString());
classChanged = true;
}
}
log.info("[{}] has secured method {}, modifying method... ",cc.getSimpleName(), ctMethods[i].getName());
//Build the code block that enforces security.
StringBuilder sb = new StringBuilder();
sb.append("{\n");
//So this does not play nice when trying to use hash sets...
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");
accessLevels.forEach(e -> {
sb.append("if(level == net.jami.jams.common.objects.user.AccessLevel.valueOf(\"").append(e.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");
ctMethods[i].insertBefore(sb.toString());
}
}
}
if(classChanged){
try {
if (cc.isFrozen()) cc.defrost();
cc.toClass();
log.info("Successfully modified class " + classname);
}
catch (Exception e){
log.error("Could not persist changes to class " + classname);
}
if(classChanged) {
if (cc.isFrozen()) cc.defrost();
cc.toClass();
}
} catch (Exception e) {
//log.info("Could not modify a target class with error {}",e.getMessage());
log.info("an error occurred!");
}
}
}
......@@ -26,6 +26,7 @@ import com.jsoniter.JsonIterator;
import javassist.ClassPool;
import lombok.extern.slf4j.Slf4j;
import net.jami.datastore.main.DataStore;
import net.jami.jams.common.annotations.ScopedServletAnnotationScanner;
import net.jami.jams.common.authentication.AuthenticationSourceType;
import net.jami.jams.common.authentication.local.LocalAuthSettings;
import net.jami.jams.common.authmodule.AuthenticationModule;
......@@ -71,6 +72,8 @@ public class Server {
public static void main(String[] args) {
//This is a fix to drop old cached stuff from the tomcat classloader.
ClassPool.getDefault().clearImportedPackages();
ScopedServletAnnotationScanner scanner = new ScopedServletAnnotationScanner();
scanner.processClasses(new java.io.File(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getName());
switch (args.length) {
case 1:
tomcatLauncher = new TomcatLauncher(Integer.parseInt(args[0]));
......
......@@ -28,6 +28,7 @@ import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.annotations.ScopedServletMethod;
import net.jami.jams.common.dao.StatementElement;
import net.jami.jams.common.dao.StatementList;
......@@ -46,6 +47,7 @@ public class DevicesServlet extends HttpServlet {
//Get a list of devices for a user.
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
StatementList statementList = new StatementList();
......
......@@ -22,17 +22,14 @@
*/
package net.jami.jams.server.servlets.api.admin.update;
import com.jsoniter.JsonIterator;
import com.jsoniter.any.Any;
import com.jsoniter.output.JsonStream;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.annotations.ScopedServletMethod;
import net.jami.jams.common.objects.contacts.Contact;
import net.jami.jams.common.objects.responses.SubscriptionStatusResponse;
import net.jami.jams.common.objects.user.AccessLevel;
import net.jami.jams.server.Server;
......@@ -47,9 +44,8 @@ public class SubscriptionServlet extends HttpServlet {
//Get the subscription status (see: SubscriptionStatusResponse.class)
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
resp.setContentType("application/json");
SubscriptionStatusResponse subscriptionStatusResponse = new SubscriptionStatusResponse();
subscriptionStatusResponse.setLicenseInformation(Server.licenseService.getLicenseInformation());
subscriptionStatusResponse.setActivated(Server.activated.get());
......@@ -61,7 +57,6 @@ public class SubscriptionServlet extends HttpServlet {
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
String license = new String(req.getInputStream().readAllBytes());
if(license != null || !license.isBlank()) {
// create .dat file to be used later
......
......@@ -28,6 +28,8 @@ import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.annotations.ScopedServletMethod;
import net.jami.jams.common.objects.user.AccessLevel;
import net.jami.jams.common.updater.FullSystemStatusResponse;
......@@ -43,6 +45,7 @@ public class UpdateServlet extends HttpServlet {
//Return the current version number and the available version number.
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
FullSystemStatusResponse response = new FullSystemStatusResponse();
response.setLocalVersions(appUpdater.getLocalVersions());
......
......@@ -28,6 +28,7 @@ import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.annotations.ScopedServletMethod;
import net.jami.jams.common.authentication.AuthenticationSourceType;
import net.jami.jams.common.dao.StatementElement;
......@@ -52,6 +53,7 @@ public class UserServlet extends HttpServlet {
//Get the user profile.
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
StatementList statementList = new StatementList();
StatementElement st1 = new StatementElement("username","=",req.getParameter("username"),"");
......
......@@ -28,6 +28,7 @@ import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.common.annotations.JsonContent;
import java.io.IOException;
......@@ -61,6 +62,7 @@ public class DirectoriesServlet extends HttpServlet {
* @apiError (500) {null} null could not return any authentication sources
*/
@Override
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().write(JsonStream.serialize(userAuthenticationModule.getAuthSources().keySet()).getBytes());
}
......
......@@ -32,6 +32,7 @@ import net.jami.jams.common.dao.StatementElement;
import net.jami.jams.common.dao.StatementList;
import net.jami.jams.common.objects.user.AccessLevel;
import net.jami.jams.common.objects.user.User;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.objects.user.UserProfile;
import java.io.IOException;
......@@ -53,6 +54,7 @@ public class SearchDirectoryServlet extends HttpServlet {
*
*/
@Override
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
List<UserProfile> userProfiles = new ArrayList<>();
userAuthenticationModule.getAuthSources().forEach((k, v) -> {
......
......@@ -28,6 +28,7 @@ import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.annotations.ScopedServletMethod;
import net.jami.jams.common.objects.user.AccessLevel;
......@@ -39,8 +40,8 @@ public class InstallLastStepServlet extends HttpServlet {
@Override
@ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
HashMap<String,String> payload = new HashMap<>();
payload.put("uri",CachedObjects.endpoint);
resp.setStatus(200);
......
......@@ -30,6 +30,7 @@ import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.common.authentication.AuthenticationSourceType;
import net.jami.jams.common.authmodule.AuthTokenResponse;
import net.jami.jams.common.objects.requests.CredentialsRequest;
......@@ -49,6 +50,7 @@ This is not scoped because it is called once.
public class StartInstallServlet extends HttpServlet {
@Override
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//Here we must decide which page to show - login or sign-up
if(dataStore != null && dataStore.getUserDao() != null && !dataStore.getUserDao().getObjects(null).isEmpty())
......@@ -58,7 +60,8 @@ public class StartInstallServlet extends HttpServlet {
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
@JsonContent
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
CredentialsRequest credentialsRequest = JsonIterator.deserialize(req.getInputStream().readAllBytes(),CredentialsRequest.class);
AuthTokenResponse res = null;
if(credentialsRequest.getUsername() != null && credentialsRequest.getPassword() != null){
......@@ -71,6 +74,7 @@ public class StartInstallServlet extends HttpServlet {
//This is the ONLY case where we write directy to the DB
@Override
@JsonContent
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if(dataStore.getUserDao().getObjects(null).size() != 0){
resp.sendError(500,"We have tried to create an administrative account where one already exists!");
......
......@@ -28,6 +28,7 @@ import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.jami.jams.ca.JamsCA;
import net.jami.jams.common.annotations.JsonContent;
import net.jami.jams.server.Server;
import java.io.IOException;
......@@ -37,8 +38,8 @@ import java.util.HashMap;
public class ServerStatusServlet extends HttpServlet {
@Override
@JsonContent
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
HashMap<String,String> statusInfo = new HashMap<>();
statusInfo.put("installed",String.valueOf(Server.isInstalled.get()));
resp.getOutputStream().write(JsonStream.serialize(statusInfo).getBytes());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment