From 9407d6a8587270ea3ab2f66d115dcfa453892653 Mon Sep 17 00:00:00 2001
From: Pascal Arlt <parlt@suse.com>
Date: Tue, 20 Feb 2024 18:02:02 +0100
Subject: [PATCH] Fix system.provisionSystem when called via HTTP API
 (bsc#1219875)

Signed-off-by: Pascal Arlt <parlt@suse.com>
---
 .../frontend/xmlrpc/system/SystemHandler.java | 88 ++++++++++++-------
 .../com/suse/manager/api/RouteFactory.java    | 15 +++-
 ...parlt.Manager-4.3-fix-provision-system-api |  1 +
 3 files changed, 71 insertions(+), 33 deletions(-)
 create mode 100644 java/spacewalk-java.changes.parlt.Manager-4.3-fix-provision-system-api

diff --git a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java
index f78304fb74b..4eb3fd8e268 100644
--- a/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java
+++ b/java/code/src/com/redhat/rhn/frontend/xmlrpc/system/SystemHandler.java
@@ -187,6 +187,7 @@
 
 import com.suse.cloud.CloudPaygManager;
 import com.suse.manager.api.ApiIgnore;
+import com.suse.manager.api.ApiType;
 import com.suse.manager.api.ReadOnly;
 import com.suse.manager.virtualization.VirtualizationActionHelper;
 import com.suse.manager.webui.controllers.virtualization.gson.VirtualGuestSetterActionJson;
@@ -230,6 +231,8 @@
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * SystemHandler
  * @apidoc.namespace system
@@ -2921,38 +2924,34 @@ public int provisionVirtualGuest(User loggedInUser, Integer sid, String guestNam
      * @apidoc.returntype #param_desc("int", "id", "ID of the action scheduled, otherwise exception thrown
      * on error")
      */
+    @ApiIgnore(ApiType.HTTP)
     public int provisionSystem(User loggedInUser, Integer sid, String profileName)
             throws FaultException {
-        log.debug("provisionSystem called.");
-
-        // Lookup the server so we can validate it exists and throw error if not.
-        Server server = lookupServer(loggedInUser, sid);
-        if (server.hasEntitlement(EntitlementManager.FOREIGN)) {
-            throw new FaultException(-2, "provisionError",
-                    "System does not have required entitlement");
-        }
-
-        KickstartData ksdata = KickstartFactory.
-                lookupKickstartDataByLabelAndOrgId(profileName,
-                        loggedInUser.getOrg().getId());
-        if (ksdata == null) {
-            throw new FaultException(-3, "kickstartProfileNotFound",
-                    "No Kickstart Profile found with label: " + profileName);
-        }
-
-        KickstartHelper helper = new KickstartHelper(RhnXmlRpcServer.getRequest());
-        String host = helper.getKickstartHost();
-
+       return provisionSystem(loggedInUser, RhnXmlRpcServer.getRequest(), sid, profileName, new Date());
+    }
 
-        KickstartScheduleCommand cmd = new KickstartScheduleCommand(
-                Long.valueOf(sid),
-                ksdata.getId(), loggedInUser, new Date(), host);
-        ValidatorError ve = cmd.store();
-        if (ve != null) {
-            throw new FaultException(-2, "provisionError",
-                    LocalizationService.getInstance().getMessage(ve.getKey()));
-        }
-        return cmd.getScheduledAction().getId().intValue();
+    /**
+     * Provision a system using the specified kickstart/autoinstallation profile.
+     *
+     * @param loggedInUser The current user
+     * @param request the spark request
+     * @param sid of the system to be provisioned
+     * @param profileName of Profile to be used.
+     * @return Returns 1 if successful, exception otherwise
+     * @throws FaultException A FaultException is thrown if the server corresponding to
+     * id cannot be found or profile is not found.
+     *
+     * @apidoc.doc Provision a system using the specified kickstart/autoinstallation profile.
+     * @apidoc.param #session_key()
+     * @apidoc.param #param_desc("int", "sid", "ID of the system to be provisioned.")
+     * @apidoc.param #param_desc("string", "profileName", "Profile to use.")
+     * @apidoc.returntype #param_desc("int", "id", "ID of the action scheduled, otherwise exception thrown
+     * on error")
+     */
+    @ApiIgnore(ApiType.XMLRPC)
+    public int provisionSystem(User loggedInUser, HttpServletRequest request, Integer sid, String profileName)
+            throws FaultException {
+        return provisionSystem(loggedInUser, request, sid, profileName, new Date());
     }
 
     /**
@@ -2974,9 +2973,37 @@ public int provisionSystem(User loggedInUser, Integer sid, String profileName)
      * @apidoc.returntype #param_desc("int", "id", "ID of the action scheduled, otherwise exception thrown
      * on error")
      */
+    @ApiIgnore(ApiType.HTTP)
     public int provisionSystem(User loggedInUser, Integer sid,
             String profileName, Date earliestDate)
                     throws FaultException {
+        return provisionSystem(loggedInUser, RhnXmlRpcServer.getRequest(), sid, profileName, earliestDate);
+    }
+
+    /**
+     * Provision a system using the specified kickstart/autoinstallation profile at specified time.
+     *
+     * @param loggedInUser The current user
+     * @param request the request
+     * @param sid of the system to be provisioned
+     * @param profileName of Profile to be used.
+     * @param earliestDate when the autoinstallation needs to be scheduled
+     * @return Returns 1 if successful, exception otherwise
+     * @throws FaultException A FaultException is thrown if the server corresponding to
+     * id cannot be found or profile is not found.
+     *
+     * @apidoc.doc Provision a system using the specified kickstart/autoinstallation profile.
+     * @apidoc.param #session_key()
+     * @apidoc.param #param_desc("int", "sid", "ID of the system to be provisioned.")
+     * @apidoc.param #param_desc("string", "profileName", "Profile to use.")
+     * @apidoc.param #param("$date", "earliestDate")
+     * @apidoc.returntype #param_desc("int", "id", "ID of the action scheduled, otherwise exception thrown
+     * on error")
+     */
+    @ApiIgnore(ApiType.XMLRPC)
+    public int provisionSystem(User loggedInUser, HttpServletRequest request, Integer sid,
+                                String profileName, Date earliestDate)
+            throws FaultException {
         log.debug("provisionSystem called.");
 
         // Lookup the server so we can validate it exists and throw error if not.
@@ -2994,9 +3021,10 @@ public int provisionSystem(User loggedInUser, Integer sid,
                     "No Kickstart Profile found with label: " + profileName);
         }
 
-        KickstartHelper helper = new KickstartHelper(RhnXmlRpcServer.getRequest());
+        KickstartHelper helper = new KickstartHelper(request);
         String host = helper.getKickstartHost();
 
+
         KickstartScheduleCommand cmd = new KickstartScheduleCommand(
                 Long.valueOf(sid),
                 ksdata.getId(), loggedInUser, earliestDate, host);
diff --git a/java/code/src/com/suse/manager/api/RouteFactory.java b/java/code/src/com/suse/manager/api/RouteFactory.java
index df278e3d306..72251342fad 100644
--- a/java/code/src/com/suse/manager/api/RouteFactory.java
+++ b/java/code/src/com/suse/manager/api/RouteFactory.java
@@ -48,6 +48,9 @@
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 
+import javax.servlet.http.HttpServletRequest;
+
+import spark.Request;
 import spark.Route;
 import spark.Spark;
 
@@ -164,7 +167,7 @@ public Route createRoute(List<Method> methods, BaseHandler handler) {
             String sessionKey = new RequestContext(req.raw()).getWebSession().getKey();
             try {
                 // Find an overload matching the parameter names and types
-                MethodCall call = findMethod(methods, requestParams, sessionKey);
+                MethodCall call = findMethod(methods, requestParams, sessionKey, req);
                 HttpApiResponse response = HttpApiResponse.success(call.invoke(handler));
                 return SparkApplicationHelper.json(gson, res, response);
             }
@@ -198,17 +201,20 @@ public Route createRoute(List<Method> methods, BaseHandler handler) {
      * @param methods list of methods
      * @param jsonArgs the JSON arguments
      * @param sessionKey the session key
+     * @param request the spark request
      * @return the matched method, if exists
      * @throws NoSuchMethodException if no match is found
      */
-    private MethodCall findMethod(List<Method> methods, Map<String, JsonElement> jsonArgs, String sessionKey)
+    private MethodCall findMethod(List<Method> methods, Map<String, JsonElement> jsonArgs,
+                                  String sessionKey, Request request)
             throws NoSuchMethodException {
         User user = SessionManager.loadSession(sessionKey).getUser();
         // Filter methods with parameter names that match the request parameters, excluding the User parameter
         return methods.stream()
                 .filter(m -> jsonArgs.keySet().equals(
                         Arrays.stream(m.getParameters())
-                                .filter(p -> !User.class.equals(p.getType()))
+                                .filter(p -> !(User.class.equals(p.getType()) ||
+                                        HttpServletRequest.class.equals(p.getType())))
                                 .map(Parameter::getName)
                                 .filter(p -> !"sessionKey".equals(p))
                                 .collect(Collectors.toSet())))
@@ -220,6 +226,9 @@ private MethodCall findMethod(List<Method> methods, Map<String, JsonElement> jso
                         if (User.class.equals(param.getType())) {
                             args.add(user);
                         }
+                        else if (HttpServletRequest.class.equals(param.getType())) {
+                            args.add(request.raw());
+                        }
                         else if ("sessionKey".equals(param.getName())) {
                             args.add(sessionKey);
                         }
diff --git a/java/spacewalk-java.changes.parlt.Manager-4.3-fix-provision-system-api b/java/spacewalk-java.changes.parlt.Manager-4.3-fix-provision-system-api
new file mode 100644
index 00000000000..c2c8edf2e11
--- /dev/null
+++ b/java/spacewalk-java.changes.parlt.Manager-4.3-fix-provision-system-api
@@ -0,0 +1 @@
+- Fix system.provisionSystem when called via HTTP API (bsc#1219875)
