org.dasein.cloud.nimbula.network.SecurityList.java Source code

Java tutorial

Introduction

Here is the source code for org.dasein.cloud.nimbula.network.SecurityList.java

Source

/**
 * Copyright (C) 2009-2012 enStratus Networks Inc
 *
 * ====================================================================
 * Licensed 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 org.dasein.cloud.nimbula.network;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;

import org.apache.log4j.Logger;
import org.dasein.cloud.CloudException;
import org.dasein.cloud.InternalException;
import org.dasein.cloud.OperationNotSupportedException;
import org.dasein.cloud.ProviderContext;
import org.dasein.cloud.Requirement;
import org.dasein.cloud.ResourceStatus;
import org.dasein.cloud.identity.ServiceAction;
import org.dasein.cloud.network.Direction;
import org.dasein.cloud.network.Firewall;
import org.dasein.cloud.network.FirewallRule;
import org.dasein.cloud.network.FirewallSupport;
import org.dasein.cloud.network.Permission;
import org.dasein.cloud.network.Protocol;
import org.dasein.cloud.network.RuleTarget;
import org.dasein.cloud.network.RuleTargetType;
import org.dasein.cloud.nimbula.NimbulaDirector;
import org.dasein.cloud.nimbula.NimbulaMethod;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class SecurityList implements FirewallSupport {
    static private final Logger logger = NimbulaDirector.getLogger(SecurityList.class);

    static public final String SECURITY_APPLICATION = "secapplication";
    static public final String SECURITY_IP_LIST = "seciplist";
    static public final String SECURITY_LIST = "seclist";
    static public final String SECURITY_RULES = "secrule";

    private NimbulaDirector provider;

    SecurityList(@Nonnull NimbulaDirector provider) {
        this.provider = provider;
    }

    private String createApplication(Protocol protocol, String dport) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_APPLICATION);
        HashMap<String, Object> state = new HashMap<String, Object>();
        String name = protocol.name().toLowerCase();

        state.put("protocol", name);
        state.put("dport", dport);
        state.put("uri", null);
        name = (name + dport.replaceAll("-", "_"));
        state.put("name", provider.getNamePrefix() + "/dsn_" + name);
        state.put("icmptype", "");
        state.put("icmpcode", "");
        method.post(state);
        try {
            JSONObject ob = method.getResponseBody();

            return ob.getString("name");
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new CloudException(e);
        }
    }

    private String createList(String cidr) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_IP_LIST);
        HashMap<String, Object> state = new HashMap<String, Object>();

        state.put("secipentries", Collections.singletonList(cidr));
        state.put("uri", null);
        state.put("name", provider.getNamePrefix() + "/dsn" + cidr.replaceAll("\\.", "_").replaceAll("/", "_"));
        method.post(state);
        try {
            JSONObject ob = method.getResponseBody();

            return ob.getString("name");
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new CloudException(e);
        }
    }

    private String getApplicationId(Protocol protocol, int startPort, int endPort, boolean create)
            throws InternalException, CloudException {
        String dport = String.valueOf(startPort);

        if (endPort > startPort) {
            dport = dport + "-" + endPort;
        }
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_APPLICATION);

        method.list();
        try {
            JSONArray array = method.getResponseBody().getJSONArray("result");

            for (int i = 0; i < array.length(); i++) {
                JSONObject ob = array.getJSONObject(i);

                if (ob.has("protocol") && ob.getString("protocol").equals(protocol.name().toLowerCase())) {
                    if (ob.has("dport") && ob.getString("dport").equals(dport)) {
                        return ob.getString("name");
                    }
                }
            }
            if (create) {
                return createApplication(protocol, dport);
            }
            return null;
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    private String getIpListId(String forCidr, boolean create) throws InternalException, CloudException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_IP_LIST);

        method.list();
        try {
            JSONArray array = method.getResponseBody().getJSONArray("result");
            String ipListId = null;

            for (int i = 0; i < array.length(); i++) {
                JSONObject ob = array.getJSONObject(i);

                if (ob.has("secipentries")) {
                    JSONArray entries = ob.getJSONArray("secipentries");

                    if (entries.length() != 1) {
                        continue;
                    }
                    String entry = entries.getString(0);

                    if (entry.equals(forCidr)) {
                        ipListId = ob.getString("name");
                    }
                }
            }
            if (ipListId == null && create) {
                ipListId = createList(forCidr);
            }
            return ipListId;
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    @Override
    public @Nonnull String authorize(@Nonnull String firewallId, @Nonnull String cidr, @Nonnull Protocol protocol,
            int beginPort, int endPort) throws CloudException, InternalException {
        return authorize(firewallId, Direction.INGRESS, cidr, protocol, beginPort, endPort);
    }

    @Override
    public @Nonnull String authorize(@Nonnull String firewallId, @Nonnull Direction direction, @Nonnull String cidr,
            @Nonnull Protocol protocol, int beginPort, int endPort) throws CloudException, InternalException {
        return authorize(firewallId, direction, Permission.ALLOW, cidr, protocol, RuleTarget.getGlobal(firewallId),
                beginPort, endPort);
    }

    @Override
    public @Nonnull String authorize(@Nonnull String firewallId, @Nonnull Direction direction,
            @Nonnull Permission permission, @Nonnull String source, @Nonnull Protocol protocol, int beginPort,
            int endPort) throws CloudException, InternalException {
        return authorize(firewallId, direction, permission, source, protocol, RuleTarget.getGlobal(firewallId),
                beginPort, endPort);
    }

    @Override
    public @Nonnull String authorize(@Nonnull String firewallId, @Nonnull Direction direction,
            @Nonnull Permission permission, @Nonnull String source, @Nonnull Protocol protocol,
            @Nonnull RuleTarget target, int beginPort, int endPort) throws CloudException, InternalException {
        if (direction.equals(Direction.INGRESS)) {
            return authorize(firewallId, direction, permission, RuleTarget.getCIDR(source), protocol, target,
                    beginPort, endPort, 0);
        } else {
            return authorize(firewallId, direction, permission, target, protocol, RuleTarget.getCIDR(source),
                    beginPort, endPort, 0);
        }
    }

    @Override
    public @Nonnull String authorize(@Nonnull String firewallId, @Nonnull Direction direction,
            @Nonnull Permission permission, @Nonnull RuleTarget sourceEndpoint, @Nonnull Protocol protocol,
            @Nonnull RuleTarget destinationEndpoint, int beginPort, int endPort, @Nonnegative int precedence)
            throws CloudException, InternalException {
        if (!direction.equals(Direction.INGRESS)
                || !sourceEndpoint.getRuleTargetType().equals(RuleTargetType.CIDR)) {
            throw new OperationNotSupportedException("Not yet supported");
        }
        //noinspection ConstantConditions
        if (!destinationEndpoint.getRuleTargetType().equals(RuleTargetType.GLOBAL)
                || !destinationEndpoint.getProviderFirewallId().equals(firewallId)) {
            throw new OperationNotSupportedException("Not yet supported");
        }
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_RULES);
        String ipListId = getIpListId(sourceEndpoint.getCidr(), true);
        String appId = getApplicationId(protocol, beginPort, endPort, true);

        String ruleId = (provider.getNamePrefix() + "/dsn_" + protocol.name() + "_" + System.currentTimeMillis()
                + "_" + beginPort + "_" + endPort);

        HashMap<String, Object> state = new HashMap<String, Object>();

        state.put("dst_list", "seclist:" + firewallId);
        state.put("src_list", "seciplist:" + ipListId);
        state.put("uri", null);
        state.put("application", appId);
        if (permission.equals(Permission.ALLOW)) {
            state.put("action", "PERMIT");
        } else {
            state.put("action", "DENY");
        }
        state.put("name", ruleId);

        method.post(state);

        return ruleId;
    }

    private String toSecurityListName(String rawName) throws CloudException, InternalException {
        StringBuilder str = new StringBuilder();

        rawName = rawName.toLowerCase();
        for (int i = 0; i < rawName.length(); i++) {
            char c = rawName.charAt(i);

            if (Character.isLetterOrDigit(c)) {
                str.append(c);
            }
        }
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context was set for this request");
        }
        if (str.length() > 0) {
            String acct = ctx.getAccountNumber();

            try {
                String user = new String(ctx.getAccessPublic(), "utf-8");

                return ("/" + acct + "/" + user + "/" + str.toString());
            } catch (UnsupportedEncodingException e) {
                throw new InternalException(e);
            }
        }
        throw new CloudException("Invalid name: " + rawName);
    }

    @Override
    public @Nonnull String create(@Nonnull String name, @Nonnull String description)
            throws InternalException, CloudException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_LIST);
        HashMap<String, Object> state = new HashMap<String, Object>();

        state.put("policy", "");
        state.put("uri", null);
        state.put("outbound_cidr_policy", "");
        state.put("name", toSecurityListName(name));

        method.post(state);

        Firewall firewall;

        try {
            firewall = toFirewall(method.getResponseBody());
            if (firewall == null) {
                throw new CloudException("No firewall was part of the response");
            }
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new CloudException(e);
        }
        //noinspection ConstantConditions
        return firewall.getProviderFirewallId();
    }

    @Override
    public @Nonnull String createInVLAN(@Nonnull String name, @Nonnull String description,
            @Nonnull String providerVlanId) throws InternalException, CloudException {
        throw new UnsupportedOperationException("VLAN security list creation is not supported");
    }

    @Override
    public void delete(@Nonnull String firewallId) throws InternalException, CloudException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_LIST);

        method.delete(firewallId);
    }

    @Override
    public @Nullable Firewall getFirewall(@Nonnull String firewallId) throws InternalException, CloudException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_LIST);
        int code = method.get(firewallId);

        if (code == 404 || code == 401) {
            return null;
        }
        try {
            return toFirewall(method.getResponseBody());
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    @Override
    public @Nonnull String getProviderTermForFirewall(@Nonnull Locale locale) {
        return "security list";
    }

    @Override
    public @Nonnull Collection<FirewallRule> getRules(@Nonnull String firewallId)
            throws InternalException, CloudException {
        // this needs refactoring
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_RULES);

        method.list();

        try {
            ArrayList<FirewallRule> rules = new ArrayList<FirewallRule>();
            JSONArray array = method.getResponseBody().getJSONArray("result");
            String id = "seclist:" + firewallId;

            for (int i = 0; i < array.length(); i++) {
                JSONObject ob = array.getJSONObject(i);

                if (ob != null) {
                    if (ob.has("dst_is_ip") && ob.getBoolean("dst_is_ip")) {
                        continue;
                    }
                    if (ob.has("dst_list") && ob.getString("dst_list").equals(id)) {
                        Collection<FirewallRule> r = toRule(firewallId, ob);

                        if (r != null) {
                            rules.addAll(r);
                        }
                    }
                }
            }
            return rules;
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    @Override
    public @Nonnull Requirement identifyPrecedenceRequirement(boolean inVlan)
            throws InternalException, CloudException {
        return Requirement.NONE;
    }

    @Override
    public @Nonnull Collection<Firewall> list() throws InternalException, CloudException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_LIST);

        method.list();
        try {
            ArrayList<Firewall> firewalls = new ArrayList<Firewall>();
            JSONArray array = method.getResponseBody().getJSONArray("result");

            for (int i = 0; i < array.length(); i++) {
                Firewall firewall = toFirewall(array.getJSONObject(i));

                if (firewall != null) {
                    firewalls.add(firewall);
                }
            }
            return firewalls;
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    @Override
    public @Nonnull Iterable<ResourceStatus> listFirewallStatus() throws InternalException, CloudException {
        ArrayList<ResourceStatus> status = new ArrayList<ResourceStatus>();

        for (Firewall fw : list()) {
            //noinspection ConstantConditions
            status.add(new ResourceStatus(fw.getProviderFirewallId(), true));
        }
        return status;
    }

    @Override
    public @Nonnull Iterable<RuleTargetType> listSupportedDestinationTypes(boolean inVlan)
            throws InternalException, CloudException {
        if (inVlan) {
            return Collections.emptyList();
        }
        return Collections.singletonList(RuleTargetType.GLOBAL);
    }

    @Override
    public @Nonnull Iterable<Direction> listSupportedDirections(boolean inVlan)
            throws InternalException, CloudException {
        if (inVlan) {
            return Collections.emptyList();
        }
        return Collections.singletonList(Direction.INGRESS);
    }

    @Override
    public @Nonnull Iterable<Permission> listSupportedPermissions(boolean inVlan)
            throws InternalException, CloudException {
        if (inVlan) {
            return Collections.emptyList();
        }
        ArrayList<Permission> permissions = new ArrayList<Permission>();

        permissions.add(Permission.ALLOW);
        permissions.add(Permission.DENY);
        return permissions;
    }

    @Override
    public @Nonnull Iterable<RuleTargetType> listSupportedSourceTypes(boolean inVlan)
            throws InternalException, CloudException {
        if (inVlan) {
            return Collections.emptyList();
        }
        return Collections.singletonList(RuleTargetType.CIDR);
    }

    @Override
    public boolean isSubscribed() throws CloudException, InternalException {
        return provider.getComputeServices().getVirtualMachineSupport().isSubscribed();
    }

    @Override
    public boolean isZeroPrecedenceHighest() throws InternalException, CloudException {
        return true;
    }

    @Override
    public @Nonnull String[] mapServiceAction(@Nonnull ServiceAction action) {
        return new String[0];
    }

    @Override
    public void revoke(@Nonnull String ruleId) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_RULES);

        method.delete(ruleId);
    }

    @Override
    public void revoke(@Nonnull String firewallId, @Nonnull String cidr, @Nonnull Protocol protocol, int beginPort,
            int endPort) throws CloudException, InternalException {
        revoke(firewallId, Direction.INGRESS, cidr, protocol, beginPort, endPort);
    }

    @Override
    public void revoke(@Nonnull String firewallId, @Nonnull Direction direction, @Nonnull String cidr,
            @Nonnull Protocol protocol, int beginPort, int endPort) throws CloudException, InternalException {
        revoke(firewallId, direction, Permission.ALLOW, cidr, protocol, RuleTarget.getGlobal(firewallId), beginPort,
                endPort);
    }

    @Override
    public void revoke(@Nonnull String firewallId, @Nonnull Direction direction, @Nonnull Permission permission,
            @Nonnull String source, @Nonnull Protocol protocol, int beginPort, int endPort)
            throws CloudException, InternalException {
        revoke(firewallId, direction, permission, source, protocol, RuleTarget.getGlobal(firewallId), beginPort,
                endPort);
    }

    @Override
    public void revoke(@Nonnull String firewallId, @Nonnull Direction direction, @Nonnull Permission permission,
            @Nonnull String source, @Nonnull Protocol protocol, @Nonnull RuleTarget target, int beginPort,
            int endPort) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_RULES);
        String ipListId = getIpListId(source, false);
        String appId = getApplicationId(protocol, beginPort, endPort, false);

        if (ipListId == null || appId == null) {
            return;
        }
        method.list();

        try {
            JSONArray array = method.getResponseBody().getJSONArray("result");
            String id = "seclist:" + firewallId;

            for (int i = 0; i < array.length(); i++) {
                JSONObject ob = array.getJSONObject(i);

                if (ob != null) {
                    if (ob.has("dst_is_ip") && ob.getBoolean("dst_is_ip")) {
                        continue;
                    }
                    if (ob.has("action") && !ob.isNull("action")) {
                        String action = ob.getString("action");

                        if (permission.equals(Permission.ALLOW) && !action.equalsIgnoreCase("permit")) {
                            continue;
                        } else if (permission.equals(Permission.DENY) && !action.equalsIgnoreCase("deny")) {
                            continue;
                        }
                    } else {
                        continue;
                    }
                    if (ob.has("dst_list") && ob.getString("dst_list").equals(id)) {
                        if (ob.has("src_list")) {
                            String listId = ob.getString("src_list");

                            if (listId.equals("seciplist:" + ipListId)) {
                                if (ob.has("application") && ob.getString("application").equals(appId)) {
                                    revoke(ob.getString("name"));
                                }
                            }
                        }
                    }
                }
            }
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    @Override
    public boolean supportsRules(@Nonnull Direction direction, @Nonnull Permission permission, boolean inVlan)
            throws CloudException, InternalException {
        return (direction.equals(Direction.INGRESS) && !inVlan);
    }

    @Override
    public boolean supportsFirewallSources() throws CloudException, InternalException {
        return false;
    }

    private @Nullable Firewall toFirewall(@Nullable JSONObject ob) throws JSONException, CloudException {
        if (ob == null) {
            return null;
        }
        ProviderContext ctx = provider.getContext();

        if (ctx == null) {
            throw new CloudException("No context was set for this request");
        }
        String regionId = ctx.getRegionId();

        if (regionId == null) {
            throw new CloudException("No region was set for this request");
        }
        String id = (ob.has("name") ? ob.getString("name") : null);

        if (id == null) {
            return null;
        }
        Firewall fw = new Firewall();

        fw.setProviderFirewallId(id);
        fw.setActive(true);
        fw.setAvailable(true);
        fw.setRegionId(regionId);

        String name = id;

        if (fw.getName() == null) {
            String[] tmp = id.split("/");

            name = (tmp == null || tmp.length < 1) ? id : tmp[tmp.length - 1];
            fw.setName(name);
        }
        if (fw.getDescription() == null) {
            fw.setDescription(name);
        }
        // TODO: "outbound_cidr_policy": "PERMIT"
        // TODO: "policy": "DENY"
        return fw;
    }

    @SuppressWarnings("ConstantConditions")
    private Collection<FirewallRule> toRule(String firewallId, JSONObject ob)
            throws JSONException, CloudException, InternalException {
        String destList = (ob.has("dst_list") ? ob.getString("dst_list") : null);
        String appId = (ob.has("application") ? ob.getString("application") : null);

        if (destList == null || appId == null) {
            return null;
        }
        if (!ob.has("name")) {
            return null;
        }
        String name = ob.getString("name");

        Direction direction;

        if (ob.has("dst_is_ip") && ob.getBoolean("dst_is_ip")) {
            direction = Direction.EGRESS;
        } else {
            direction = Direction.INGRESS;
        }
        Permission permission = Permission.ALLOW;

        if (ob.has("action")) {
            permission = (ob.getString("action").equalsIgnoreCase("permit") ? Permission.ALLOW : Permission.DENY);
        }
        JSONObject app = getSecurityApplication(appId);
        int startPort, endPort;
        Protocol protocol;

        if (app == null) {
            return null;
        } else {
            String p = app.getString("protocol");

            protocol = Protocol.valueOf(p.toUpperCase());
            if (app.has("dport")) {
                String dport = app.getString("dport");

                if (dport != null && !dport.equals("")) {
                    int idx = dport.indexOf('-');

                    if (idx < 1) {
                        startPort = Integer.parseInt(dport);
                        endPort = Integer.parseInt(dport);
                    } else {
                        String s = dport.substring(0, idx);
                        String e = dport.substring(idx + 1);

                        startPort = Integer.parseInt(s);
                        endPort = Integer.parseInt(e);
                    }
                } else {
                    startPort = -1;
                    endPort = -1;
                }
            } else {
                startPort = -1;
                endPort = -1;
            }
        }
        FirewallRule rule = FirewallRule.getInstance(null, firewallId, RuleTarget.getCIDR("192.268.1.1/32"),
                direction, protocol, permission, RuleTarget.getGlobal(firewallId), startPort, endPort);
        ArrayList<FirewallRule> rules = new ArrayList<FirewallRule>();

        if (ob.has("src_is_ip") && ob.getBoolean("src_is_ip")) {
            String ipList = (ob.has("src_list") ? ob.getString("src_list") : null);

            if (ipList == null) {
                return null;
            }
            JSONObject l = getSecurityIpList(ipList.substring("seciplist:".length()));

            if (l == null) {
                return null;
            }
            JSONArray entries = (l.has("secipentries") ? l.getJSONArray("secipentries") : null);

            if (entries == null) {
                return null;
            }
            for (int i = 0; i < entries.length(); i++) {
                String entry = entries.getString(i);

                if (entry != null) {
                    rules.add(FirewallRule.getInstance(rule.getProviderRuleId() + ":" + entry, rule.getFirewallId(),
                            RuleTarget.getCIDR(entry), rule.getDirection(), rule.getProtocol(),
                            rule.getPermission(), RuleTarget.getGlobal(firewallId), rule.getStartPort(),
                            rule.getEndPort()));
                }
            }
        } else {
            // TODO: implement me
        }
        return rules;
    }

    private JSONObject getSecurityApplication(String secIpListId) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_APPLICATION);
        int code = method.get(secIpListId);

        if (code == 404 || code == 401) {
            return null;
        }
        try {
            return method.getResponseBody();
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }

    private JSONObject getSecurityIpList(String secIpListId) throws CloudException, InternalException {
        NimbulaMethod method = new NimbulaMethod(provider, SECURITY_IP_LIST);
        int code = method.get(secIpListId);

        if (code == 404 || code == 401) {
            return null;
        }
        try {
            return method.getResponseBody();
        } catch (JSONException e) {
            if (logger.isDebugEnabled()) {
                logger.error("Error parsing JSON: " + e.getMessage());
                e.printStackTrace();
            }
            throw new InternalException(e);
        }
    }
}