You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by GitBox <gi...@apache.org> on 2022/07/27 15:00:30 UTC

[GitHub] [cloudstack] nvazquez commented on a diff in pull request #6577: New API: createConsoleURL

nvazquez commented on code in PR #6577:
URL: https://github.com/apache/cloudstack/pull/6577#discussion_r931173846


##########
server/src/main/java/com/cloud/consoleproxy/ConsoleAccessManagerImpl.java:
##########
@@ -0,0 +1,459 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.consoleproxy;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.GetVmVncTicketAnswer;
+import com.cloud.agent.api.GetVmVncTicketCommand;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.HostVO;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.resource.ResourceState;
+import com.cloud.server.ManagementServer;
+import com.cloud.servlet.ConsoleProxyClientParam;
+import com.cloud.servlet.ConsoleProxyPasswordBasedEncryptor;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.EntityManager;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmDetailVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineManager;
+import com.cloud.vm.VmDetailConstants;
+import com.cloud.vm.dao.UserVmDetailsDao;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.apache.cloudstack.consoleproxy.ConsoleAccessManager;
+import org.apache.cloudstack.framework.security.keys.KeysManager;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+import java.net.InetAddress;
+import java.util.Date;
+import java.util.Map;
+
+public class ConsoleAccessManagerImpl extends ManagerBase implements ConsoleAccessManager {
+
+    @Inject
+    private AccountManager _accountMgr;
+    @Inject
+    private VirtualMachineManager _vmMgr;
+    @Inject
+    private ManagementServer _ms;
+    @Inject
+    private EntityManager _entityMgr;
+    @Inject
+    private UserVmDetailsDao _userVmDetailsDao;
+    @Inject
+    private KeysManager _keysMgr;
+    @Inject
+    private AgentManager agentManager;
+
+    private static KeysManager s_keysMgr;
+    private final Gson _gson = new GsonBuilder().create();
+
+    public static final Logger s_logger = Logger.getLogger(ConsoleAccessManagerImpl.class.getName());
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        s_keysMgr = _keysMgr;
+        return super.configure(name, params);
+    }
+
+    @Override
+    public Pair<Boolean, String> generateConsoleUrl(Long vmId) {
+        try {
+            if (_accountMgr == null || _vmMgr == null || _ms == null) {
+                return new Pair<>(false, "Service is not ready");
+            }
+
+            if (_keysMgr.getHashKey() == null) {
+                String msg = "Console/thumbnail access denied. Ticket service is not ready yet";
+                s_logger.debug(msg);
+                return new Pair<>(false, msg);
+            }
+
+            String userId = null;
+            String account = null;
+            Account accountObj = null;
+
+//            Map<String, Object[]> params = new HashMap<String, Object[]>();
+//            params.putAll(req.getParameterMap());
+//
+//            HttpSession session = req.getSession(false);
+//            if (session == null) {
+//                if (verifyRequest(params)) {
+//                    userId = (String)params.get("userid")[0];
+//                    account = (String)params.get("account")[0];
+//                    accountObj = (Account)params.get("accountobj")[0];
+//                } else {
+//                    s_logger.debug("Invalid web session or API key in request, reject console/thumbnail access");
+//                    sendResponse(resp, "Access denied. Invalid web session or API key in request");
+//                    return;
+//                }
+//            } else {
+//                // adjust to latest API refactoring changes
+//                if (session.getAttribute("userid") != null) {
+//                    userId = ((Long)session.getAttribute("userid")).toString();
+//                }
+//
+//                accountObj = (Account)session.getAttribute("accountobj");
+//                if (accountObj != null) {
+//                    account = "" + accountObj.getId();
+//                }
+//            }
+
+//            // Do a sanity check here to make sure the user hasn't already been deleted
+//            if ((userId == null) || (account == null) || (accountObj == null) || !verifyUser(Long.valueOf(userId))) {
+//                s_logger.debug("Invalid user/account, reject console/thumbnail access");
+//                sendResponse(resp, "Access denied. Invalid or inconsistent account is found");
+//                return;
+//            }
+//
+//            String cmd = req.getParameter("cmd");
+//            if (cmd == null || !isValidCmd(cmd)) {
+//                s_logger.debug("invalid console servlet command: " + cmd);
+//                sendResponse(resp, "");
+//                return;
+//            }
+//
+//            String vmIdString = req.getParameter("vm");
+            VirtualMachine vm = _entityMgr.findById(VirtualMachine.class, vmId);
+            if (vm == null) {
+                s_logger.info("invalid console servlet command parameter: " + vmId);
+                return new Pair<>(false, "Cannot find VM with ID " + vmId);
+            }
+
+//            if (!checkSessionPermision(req, vmId, accountObj)) {
+//                sendResponse(resp, "Permission denied");
+//                return;
+//            }
+
+//            if (cmd.equalsIgnoreCase("thumbnail")) {
+//                handleThumbnailRequest(req, resp, vmId);
+//            } else if (cmd.equalsIgnoreCase("access")) {
+//                handleAccessRequest(req, resp, vmId);
+//            } else {
+//                handleAuthRequest(req, resp, vmId);
+//            }
+            return new Pair<>(true, generateAccessUrl(vmId));
+        } catch (Throwable e) {
+            s_logger.error("Unexepected exception in ConsoleProxyServlet", e);
+            return new Pair<>(false, "Server Internal Error: " + e.getMessage());
+        }
+    }
+
+    private String generateAccessUrl(Long vmId) {
+        VirtualMachine vm = _vmMgr.findById(vmId);
+        String msg;
+        if (vm == null) {
+            msg = "VM " + vmId + " does not exist, sending blank response for console access request";
+            s_logger.warn(msg);
+            throw new CloudRuntimeException(msg);
+        }
+
+        if (vm.getHostId() == null) {
+            msg = "VM " + vmId + " lost host info, sending blank response for console access request";
+            s_logger.warn(msg);
+            throw new CloudRuntimeException(msg);
+        }
+
+        HostVO host = _ms.getHostBy(vm.getHostId());
+        if (host == null) {
+            msg = "VM " + vmId + "'s host does not exist, sending blank response for console access request";
+            s_logger.warn(msg);
+            throw new CloudRuntimeException(msg);
+        }
+
+        if (Hypervisor.HypervisorType.LXC.equals(vm.getHypervisorType())) {
+            throw new CloudRuntimeException("Console access is not supported for LXC");
+        }
+
+        String rootUrl = _ms.getConsoleAccessUrlRoot(vmId);
+        if (rootUrl == null) {
+            throw new CloudRuntimeException("Console access will be ready in a few minutes. Please try it again later.");
+        }
+
+        String vmName = vm.getHostName();
+        if (vm.getType() == VirtualMachine.Type.User) {
+            UserVm userVm = _entityMgr.findById(UserVm.class, vmId);
+            String displayName = userVm.getDisplayName();
+            if (displayName != null && !displayName.isEmpty() && !displayName.equals(vmName)) {
+                vmName += "(" + displayName + ")";
+            }
+        }
+
+//        InetAddress remoteAddress = null;
+//        try {
+//            remoteAddress = ApiServlet.getClientAddress(req);
+//        } catch (UnknownHostException e) {
+//            s_logger.warn("UnknownHostException when trying to lookup remote IP-Address. This should never happen. Blocking request.", e);
+//        }
+
+        String url = composeConsoleAccessUrl(rootUrl, vm, host, null);
+        s_logger.debug("The console URL is: " + url);
+        return url;
+    }
+
+    public static final String escapeHTML(String content) {
+        if (content == null || content.isEmpty())
+            return content;
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < content.length(); i++) {
+            char c = content.charAt(i);
+            switch (c) {
+                case '<':
+                    sb.append("&lt;");
+                    break;
+                case '>':
+                    sb.append("&gt;");
+                    break;
+                case '&':
+                    sb.append("&amp;");
+                    break;
+                case '"':
+                    sb.append("&quot;");
+                    break;
+                case ' ':
+                    sb.append("&nbsp;");
+                    break;
+                default:
+                    sb.append(c);
+                    break;
+            }
+        }
+        return sb.toString();
+    }
+
+    private String composeConsoleAccessUrl(String rootUrl, VirtualMachine vm, HostVO hostVo, InetAddress addr) {

Review Comment:
   Yes, the goal is to move the code away from the servlet in favour of the API implementation. Please note this PR is still in progress, will be polishing it



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@cloudstack.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org