package org.ovirt.engine.core.services;

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.ovirt.engine.core.bll.interfaces.BackendInternal;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.UserProfile;
import org.ovirt.engine.core.common.businessentities.aaa.DbUser;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryParametersBase;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.utils.crypt.EngineEncryptionUtils;
import org.ovirt.engine.core.uutils.crypto.ticket.TicketDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class VMConsoleProxyServlet extends HttpServlet {

    @Inject
    private BackendInternal backend;

    private static final String VM_CONSOLE_PROXY_EKU = "1.2.1.1";

    private static final Logger log = LoggerFactory.getLogger(VMConsoleProxyServlet.class);

    // TODO: implmement key filtering based on input parameters
    private List<Map<String, String>> availablePublicKeys(String keyFingerPrint, String keyType, String keyContent) {

        List<Map<String, String>> jsonUsers = new ArrayList<>();
        VdcQueryParametersBase userProfileParams = new VdcQueryParametersBase();

        VdcQueryReturnValue v = backend.runInternalQuery(VdcQueryType.GetAllUserProfiles, userProfileParams);

        if (v != null) {
            List<UserProfile> profiles = v.getReturnValue();

            for (UserProfile profile : profiles) {
                if (StringUtils.isNotEmpty(profile.getSshPublicKey())) {
                    IdQueryParameters userparams = new IdQueryParameters(profile.getUserId());

                    // TODO: avoid one query per loop. Bulk query?
                    VdcQueryReturnValue ret = backend.runInternalQuery(VdcQueryType.GetDbUserByUserId, userparams);
                    DbUser user = ret.getReturnValue();

                    Map<String, String> jsonUser = new HashMap<String, String>();
                    jsonUser.put("entityid", profile.getUserId().toString());
                    jsonUser.put("entity", "user-id");
                    if (user != null) {
                        jsonUser.put("username", user.getLoginName());
                    }
                    jsonUser.put("key", profile.getSshPublicKey());

                    jsonUsers.add(jsonUser);
                }
            }
        }

        return jsonUsers;
    }

    private List<Map<String, String>> availableConsoles(String userIdAsString) {

        List<Map<String, String>> jsonVms = new ArrayList<>();

        Guid userGuid = null;

        try {
            if (StringUtils.isNotEmpty(userIdAsString)) {
                userGuid = Guid.createGuidFromString(userIdAsString);
            }
        } catch (IllegalArgumentException e) {
            log.debug("Could not read User GUID");
        }

        if (userGuid != null) {
            IdQueryParameters userParam = new IdQueryParameters(userGuid);

            VdcQueryReturnValue retVms = backend.runInternalQuery(VdcQueryType.GetAllVmsForAnotherUser, userParam);

            if (retVms != null) {
                List<VM> vmsList = retVms.getReturnValue();


                for (VM vm : vmsList) {
                    Map<String, String> jsonVm = new HashMap<String, String>();

                    IdQueryParameters vmParam = new IdQueryParameters(vm.getId());

                    // TODO: avoid one query per loop. Bulk query?
                    VdcQueryReturnValue retAddr = backend.runInternalQuery(VdcQueryType.GetManagementInterfaceAddressByVmId, vmParam);

                    if (retAddr != null && retAddr.getReturnValue() != null) {
                        String vdsAddress = (String) retAddr.getReturnValue();

                        jsonVm.put("vmid", vm.getId().toString());
                        jsonVm.put("vmname", vm.getName());
                        jsonVm.put("host", vdsAddress);
                        /* there is only one serial console, no need and no way to distinguish them */
                        jsonVm.put("console", "default");

                        jsonVms.add(jsonVm);
                    }
                }
            }
        }

        return jsonVms;
    }

    // Caller must ensure to close the #body to avoid resource leaking.
    // Recommended way is to use this helper inside a try-with-resources block.
    private String readBody(BufferedReader body) throws IOException {
        StringBuilder buffer = new StringBuilder();

        int r;
        while ((r = body.read()) != -1) {
            buffer.append((char) r);
        }

        return buffer.toString();
    }

    private String validateTicket(String ticket) throws GeneralSecurityException, IOException {
        TicketDecoder ticketDecoder = new TicketDecoder(EngineEncryptionUtils.getTrustStore(),
                                                        VM_CONSOLE_PROXY_EKU,
                                                        Config.<Integer> getValue(ConfigValues.VMConsoleTicketTolerance));
        return ticketDecoder.decode(ticket);
    }

    private Map<String, Object> buildResult(String content_type, String content_id, Object content) {
        Map<String, Object> result = new HashMap<String, Object>();
        result.put("version", "1");
        result.put("content", content_type);
        result.put(content_id, content);
        return result;
    }

    private Map<String, Object> produceContentFromParameters(Map<String, String> parameters) {
        String command = parameters.get("command");
        String version = parameters.get("version");

        Map<String, Object> result = null;

        if ("1".equals(version)) {
            if ("available_consoles".equals(command)) {
                String userId = parameters.get("user_id");

                result = buildResult("console_list",
                                     "consoles",
                                     availableConsoles(userId));
            } else if ("public_keys".equals(command)) {
                String keyFingerPrint = parameters.get("key_fp");
                String keyType = parameters.get("key_type");
                String keyContent = parameters.get("key_content");

                result = buildResult("key_list",
                                     "keys",
                                     availablePublicKeys(
                                        (keyFingerPrint != null) ? keyFingerPrint : "",
                                        (keyType != null) ? keyType : "",
                                        (keyContent != null) ? keyContent : ""));
            } else {
                log.error("Unknown command: ", command);
            }
        } else {
            log.error("Unsupported version: ", version);
        }

        return result;
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try (OutputStream outputStream = response.getOutputStream();
             BufferedReader requestReader = request.getReader()) {

            String stringParameters = validateTicket(readBody(requestReader));

            ObjectMapper mapper = new ObjectMapper();

            Map<String, String> parameters = mapper.readValue(
                    stringParameters,
                    TypeFactory.defaultInstance().constructMapType(HashMap.class, String.class, String.class)
            );

            Map<String, Object> result = produceContentFromParameters(parameters);

            if (result != null) {
                response.setContentType("application/json");
                mapper.writeValue(outputStream, result);
            } else {
                response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
            }
        } catch (GeneralSecurityException e) {
            log.error("Error validating ticket: ", e);
            response.setStatus(HttpURLConnection.HTTP_FORBIDDEN);
        } catch (IOException e) {
            log.error("Error decoding ticket: ", e);
            response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
        } catch (Exception e) {
            log.error("Error processing request: ", e);
            response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
        }
    }
}

@@ -1651,16 +1651,36 @@
 KU = %221.
+3.6.1.4.1.2312.13.1.
 2.1.1%22;%0A

fix EKU for VMConsole servlet