All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zaproxy.zap.extension.api.ContextAPI Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2013 The ZAP Development Team
 *
 * 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.zaproxy.zap.extension.api;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.URI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.model.SiteNode;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import org.zaproxy.zap.authentication.AuthenticationMethod;
import org.zaproxy.zap.authentication.AuthenticationMethod.AuthCheckingStrategy;
import org.zaproxy.zap.authentication.AuthenticationMethod.AuthPollFrequencyUnits;
import org.zaproxy.zap.authentication.AuthenticationMethodType;
import org.zaproxy.zap.extension.api.ApiException.Type;
import org.zaproxy.zap.extension.authorization.AuthorizationDetectionMethod;
import org.zaproxy.zap.model.Context;
import org.zaproxy.zap.model.IllegalContextNameException;
import org.zaproxy.zap.model.Tech;
import org.zaproxy.zap.model.TechSet;
import org.zaproxy.zap.utils.ApiUtils;
import org.zaproxy.zap.utils.JsonUtil;
import org.zaproxy.zap.utils.XMLStringUtil;

public class ContextAPI extends ApiImplementor {

    private static final Logger LOGGER = LogManager.getLogger(ContextAPI.class);

    private static final String PREFIX = "context";
    private static final String TECH_NAME = "technologyName";
    private static final String ACTION_EXCLUDE_FROM_CONTEXT_REGEX = "excludeFromContext";
    private static final String ACTION_INCLUDE_IN_CONTEXT_REGEX = "includeInContext";
    private static final String ACTION_SET_CONTEXT_REGEXS = "setContextRegexs";
    private static final String ACTION_SET_CONTEXT_CHECKING_STRATEGY = "setContextCheckingStrategy";
    private static final String ACTION_NEW_CONTEXT = "newContext";
    private static final String ACTION_REMOVE_CONTEXT = "removeContext";
    private static final String ACTION_SET_CONTEXT_IN_SCOPE = "setContextInScope";
    private static final String ACTION_EXPORT_CONTEXT = "exportContext";
    private static final String ACTION_IMPORT_CONTEXT = "importContext";
    private static final String ACTION_INCLUDE_TECHS = "includeContextTechnologies";
    private static final String ACTION_INCLUDE_ALL_TECHS = "includeAllContextTechnologies";
    private static final String ACTION_EXCLUDE_TECHS = "excludeContextTechnologies";
    private static final String ACTION_EXCLUDE_ALL_TECHS = "excludeAllContextTechnologies";
    private static final String VIEW_EXCLUDE_REGEXS = "excludeRegexs";
    private static final String VIEW_INCLUDE_REGEXS = "includeRegexs";
    private static final String VIEW_CONTEXT_LIST = "contextList";
    private static final String VIEW_CONTEXT = "context";
    private static final String VIEW_ALL_TECHS = "technologyList";
    private static final String VIEW_INCLUDED_TECHS = "includedTechnologyList";
    private static final String VIEW_EXCLUDED_TECHS = "excludedTechnologyList";
    private static final String VIEW_URLS = "urls";
    private static final String REGEX_PARAM = "regex";
    private static final String INC_REGEXS_PARAM = "incRegexs";
    private static final String EXC_REGEXS_PARAM = "excRegexs";
    private static final String CONTEXT_NAME = "contextName";
    private static final String IN_SCOPE = "booleanInScope";
    private static final String CONTEXT_FILE_PARAM = "contextFile";
    private static final String CONTEXT_ID = "contextId";
    private static final String PARAM_TECH_NAMES = "technologyNames";
    private static final String PARAM_CHECKING_STRATEGRY = "checkingStrategy";
    private static final String PARAM_POLL_URL = "pollUrl";
    private static final String PARAM_POLL_DATA = "pollData";
    private static final String PARAM_POLL_HEADERS = "pollHeaders";
    private static final String PARAM_POLL_FREQ = "pollFrequency";
    private static final String PARAM_POLL_FREQ_UNITS = "pollFrequencyUnits";

    public ContextAPI() {
        List contextNameAndRegexParam = new ArrayList<>(2);
        contextNameAndRegexParam.add(CONTEXT_NAME);
        contextNameAndRegexParam.add(REGEX_PARAM);
        List contextNameOnlyParam = new ArrayList<>(1);
        contextNameOnlyParam.add((CONTEXT_NAME));
        String[] contextNameAndTechNames = new String[] {CONTEXT_NAME, PARAM_TECH_NAMES};

        this.addApiAction(
                new ApiAction(ACTION_EXCLUDE_FROM_CONTEXT_REGEX, contextNameAndRegexParam));
        this.addApiAction(new ApiAction(ACTION_INCLUDE_IN_CONTEXT_REGEX, contextNameAndRegexParam));
        this.addApiAction(
                new ApiAction(
                        ACTION_SET_CONTEXT_REGEXS,
                        new String[] {CONTEXT_NAME, INC_REGEXS_PARAM, EXC_REGEXS_PARAM}));
        this.addApiAction(
                new ApiAction(
                        ACTION_SET_CONTEXT_CHECKING_STRATEGY,
                        new String[] {CONTEXT_NAME, PARAM_CHECKING_STRATEGRY},
                        new String[] {
                            PARAM_POLL_URL,
                            PARAM_POLL_DATA,
                            PARAM_POLL_HEADERS,
                            PARAM_POLL_FREQ,
                            PARAM_POLL_FREQ_UNITS
                        }));
        this.addApiAction(new ApiAction(ACTION_NEW_CONTEXT, contextNameOnlyParam));
        this.addApiAction(new ApiAction(ACTION_REMOVE_CONTEXT, contextNameOnlyParam));
        this.addApiAction(
                new ApiAction(
                        ACTION_EXPORT_CONTEXT,
                        new String[] {CONTEXT_NAME, CONTEXT_FILE_PARAM},
                        null));
        this.addApiAction(
                new ApiAction(ACTION_IMPORT_CONTEXT, new String[] {CONTEXT_FILE_PARAM}, null));
        this.addApiAction(new ApiAction(ACTION_INCLUDE_TECHS, contextNameAndTechNames));
        this.addApiAction(new ApiAction(ACTION_INCLUDE_ALL_TECHS, contextNameOnlyParam));
        this.addApiAction(new ApiAction(ACTION_EXCLUDE_TECHS, contextNameAndTechNames));
        this.addApiAction(new ApiAction(ACTION_EXCLUDE_ALL_TECHS, contextNameOnlyParam));

        List contextInScopeParams = new ArrayList<>(2);
        contextInScopeParams.add(CONTEXT_NAME);
        contextInScopeParams.add(IN_SCOPE);
        this.addApiAction(new ApiAction(ACTION_SET_CONTEXT_IN_SCOPE, contextInScopeParams));

        this.addApiView(new ApiView(VIEW_CONTEXT_LIST));
        this.addApiView(new ApiView(VIEW_EXCLUDE_REGEXS, contextNameOnlyParam));
        this.addApiView(new ApiView(VIEW_INCLUDE_REGEXS, contextNameOnlyParam));
        this.addApiView(new ApiView(VIEW_CONTEXT, contextNameOnlyParam));
        this.addApiView(new ApiView(VIEW_ALL_TECHS));
        this.addApiView(new ApiView(VIEW_INCLUDED_TECHS, contextNameOnlyParam));
        this.addApiView(new ApiView(VIEW_EXCLUDED_TECHS, contextNameOnlyParam));
        this.addApiView(new ApiView(VIEW_URLS, contextNameOnlyParam));
    }

    @Override
    public String getPrefix() {
        return PREFIX;
    }

    @Override
    public ApiResponse handleApiAction(String name, JSONObject params) throws ApiException {
        LOGGER.debug("handleApiAction {} {}", name, params);

        Context context;
        TechSet techSet;
        String[] techNames;
        String filename;
        File f;

        switch (name) {
            case ACTION_EXCLUDE_FROM_CONTEXT_REGEX:
                try {
                    addExcludeToContext(getContext(params), params.getString(REGEX_PARAM));
                } catch (IllegalArgumentException e) {
                    throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, REGEX_PARAM, e);
                }
                break;
            case ACTION_INCLUDE_IN_CONTEXT_REGEX:
                try {
                    addIncludeToContext(getContext(params), params.getString(REGEX_PARAM));
                } catch (IllegalArgumentException e) {
                    throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, REGEX_PARAM, e);
                }
                break;
            case ACTION_SET_CONTEXT_REGEXS:
                context = getContext(params);
                JSONArray incRegexs;
                JSONArray excRegexs;
                try {
                    incRegexs = JSONArray.fromObject(params.get(INC_REGEXS_PARAM));
                    context.setIncludeInContextRegexs(JsonUtil.toStringList(incRegexs));
                } catch (JSONException e1) {
                    throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, INC_REGEXS_PARAM);
                }
                try {
                    excRegexs = JSONArray.fromObject(params.get(EXC_REGEXS_PARAM));
                    context.setExcludeFromContextRegexs(JsonUtil.toStringList(excRegexs));
                } catch (Exception e1) {
                    throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, EXC_REGEXS_PARAM);
                }
                Model.getSingleton().getSession().saveContext(context);
                break;
            case ACTION_SET_CONTEXT_CHECKING_STRATEGY:
                context = getContext(params);
                AuthCheckingStrategy checkingStrategy;
                try {
                    checkingStrategy =
                            AuthCheckingStrategy.valueOf(
                                    params.getString(PARAM_CHECKING_STRATEGRY));
                } catch (Exception e1) {
                    throw new ApiException(
                            ApiException.Type.ILLEGAL_PARAMETER, PARAM_CHECKING_STRATEGRY);
                }
                if (AuthCheckingStrategy.POLL_URL.equals(checkingStrategy)) {
                    AuthPollFrequencyUnits units;
                    try {
                        units =
                                AuthPollFrequencyUnits.valueOf(
                                        params.getString(PARAM_POLL_FREQ_UNITS));
                    } catch (Exception e) {
                        throw new ApiException(
                                ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ_UNITS);
                    }
                    int freq;
                    String pollUrl = params.getString(PARAM_POLL_URL);
                    String pollData = params.getString(PARAM_POLL_DATA);
                    String pollHeaders = params.getString(PARAM_POLL_HEADERS);
                    if (pollUrl == null || pollUrl.isEmpty()) {
                        throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_URL);
                    }
                    try {
                        new URI(pollUrl, true);
                    } catch (Exception e) {
                        throw new ApiException(ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_URL);
                    }
                    try {
                        freq = params.getInt(PARAM_POLL_FREQ);
                    } catch (Exception e) {
                        throw new ApiException(
                                ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ);
                    }
                    if (freq <= 0) {
                        throw new ApiException(
                                ApiException.Type.ILLEGAL_PARAMETER, PARAM_POLL_FREQ);
                    }
                    context.getAuthenticationMethod().setPollUrl(pollUrl);
                    context.getAuthenticationMethod().setPollData(pollData);
                    context.getAuthenticationMethod().setPollHeaders(pollHeaders);
                    context.getAuthenticationMethod().setPollFrequency(freq);
                    context.getAuthenticationMethod().setPollFrequencyUnits(units);
                }
                context.getAuthenticationMethod().setAuthCheckingStrategy(checkingStrategy);
                Model.getSingleton().getSession().saveContext(context);
                break;
            case ACTION_NEW_CONTEXT:
                String contextName = params.getString(CONTEXT_NAME);
                try {
                    context = Model.getSingleton().getSession().getNewContext(contextName);
                } catch (IllegalContextNameException e) {
                    throw new ApiException(ApiException.Type.ALREADY_EXISTS, contextName, e);
                }
                Model.getSingleton().getSession().saveContext(context);
                return new ApiResponseElement(CONTEXT_ID, String.valueOf(context.getId()));
            case ACTION_REMOVE_CONTEXT:
                context = getContext(params);
                Model.getSingleton().getSession().deleteContext(context);
                break;
            case ACTION_SET_CONTEXT_IN_SCOPE:
                context = getContext(params);
                context.setInScope(params.getBoolean(IN_SCOPE));
                Model.getSingleton().getSession().saveContext(context);
                break;
            case ACTION_IMPORT_CONTEXT:
                filename = params.getString(CONTEXT_FILE_PARAM);
                f = new File(filename);
                if (!f.exists()) {
                    // Try relative to the contexts dir
                    f = new File(Constant.getContextsDir(), filename);
                }
                if (!f.exists()) {
                    throw new ApiException(ApiException.Type.DOES_NOT_EXIST, f.getAbsolutePath());
                } else {
                    try {
                        context = Model.getSingleton().getSession().importContext(f);
                    } catch (IllegalContextNameException e) {
                        throw new ApiException(ApiException.Type.BAD_EXTERNAL_DATA, e);
                    } catch (Exception e) {
                        LOGGER.error(e.getMessage(), e);
                        throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage());
                    }
                }
                return new ApiResponseElement(CONTEXT_ID, String.valueOf(context.getId()));
            case ACTION_EXPORT_CONTEXT:
                filename = params.getString(CONTEXT_FILE_PARAM);
                context = getContext(params);

                f = new File(filename);
                if (!f.getAbsolutePath().equals(filename)) {
                    // Not an absolute filename, use one relative to the contexts dir
                    f = new File(Constant.getContextsDir(), filename);
                }
                if (!f.getParentFile().canWrite()) {
                    // Cant write to the parent dir so not looking good
                    throw new ApiException(ApiException.Type.NO_ACCESS, f.getAbsolutePath());
                } else {
                    try {
                        Model.getSingleton().getSession().exportContext(context, f);
                    } catch (Exception e) {
                        throw new ApiException(ApiException.Type.INTERNAL_ERROR, e.getMessage());
                    }
                }
                break;
            case ACTION_INCLUDE_TECHS:
                context = getContext(params);
                techSet = context.getTechSet();
                techNames = getParam(params, PARAM_TECH_NAMES, "").split(",");
                for (String techName : techNames) {
                    techSet.include(getTech(techName));
                }
                context.save();
                break;
            case ACTION_INCLUDE_ALL_TECHS:
                context = getContext(params);
                techSet = new TechSet(Tech.getAll());
                context.setTechSet(techSet);
                context.save();
                break;
            case ACTION_EXCLUDE_TECHS:
                context = getContext(params);
                techSet = context.getTechSet();
                techNames = getParam(params, PARAM_TECH_NAMES, "").split(",");
                for (String techName : techNames) {
                    techSet.exclude(getTech(techName));
                }
                context.save();
                break;
            case ACTION_EXCLUDE_ALL_TECHS:
                context = getContext(params);
                techSet = context.getTechSet();
                for (Tech tech : Tech.getAll()) {
                    techSet.exclude(tech);
                }
                context.save();
                break;
            default:
                throw new ApiException(Type.BAD_ACTION);
        }

        return ApiResponseElement.OK;
    }

    private void addExcludeToContext(Context context, String regex) {
        List incRegexes = new ArrayList<>(context.getIncludeInContextRegexs());
        if (incRegexes.remove(regex)) {
            // Its already explicitly included, removing it from the include list is safer and more
            // useful
            context.setIncludeInContextRegexs(incRegexes);
        } else {
            context.addExcludeFromContextRegex(regex);
        }
        Model.getSingleton().getSession().saveContext(context);
    }

    private void addIncludeToContext(Context context, String regex) {
        context.addIncludeInContextRegex(regex);
        Model.getSingleton().getSession().saveContext(context);
    }

    @Override
    public ApiResponse handleApiView(String name, JSONObject params) throws ApiException {
        LOGGER.debug("handleApiView {} {}", name, params);

        ApiResponse result;
        ApiResponseList resultList;
        TechSet techSet;

        switch (name) {
            case VIEW_EXCLUDE_REGEXS:
                resultList = new ApiResponseList(name);
                for (String regex : getContext(params).getExcludeFromContextRegexs()) {
                    resultList.addItem(new ApiResponseElement(REGEX_PARAM, regex));
                }
                result = resultList;
                break;
            case VIEW_INCLUDE_REGEXS:
                resultList = new ApiResponseList(name);
                for (String regex : getContext(params).getIncludeInContextRegexs()) {
                    resultList.addItem(new ApiResponseElement(REGEX_PARAM, regex));
                }
                result = resultList;
                break;
            case VIEW_CONTEXT_LIST:
                resultList = new ApiResponseList(name);
                for (Context context : Model.getSingleton().getSession().getContexts()) {
                    resultList.addItem(new ApiResponseElement(CONTEXT_NAME, context.getName()));
                }
                result = resultList;
                break;
            case VIEW_CONTEXT:
                result = new ApiResponseElement(buildResponseFromContext(getContext(params)));
                break;
            case VIEW_ALL_TECHS:
                resultList = new ApiResponseList(name);
                for (Tech tech : Tech.getAll()) {
                    resultList.addItem(new ApiResponseElement(TECH_NAME, tech.toString()));
                }
                result = resultList;
                break;
            case VIEW_INCLUDED_TECHS:
                resultList = new ApiResponseList(name);
                techSet = getContext(params).getTechSet();
                for (Tech tech : techSet.getIncludeTech()) {
                    resultList.addItem(new ApiResponseElement(TECH_NAME, tech.toString()));
                }
                result = resultList;
                break;
            case VIEW_EXCLUDED_TECHS:
                resultList = new ApiResponseList(name);
                techSet = getContext(params).getTechSet();
                for (Tech tech : techSet.getExcludeTech()) {
                    resultList.addItem(new ApiResponseElement(TECH_NAME, tech.toString()));
                }
                result = resultList;
                break;
            case VIEW_URLS:
                resultList = new ApiResponseList(name);
                Set addedUrls = new HashSet<>();
                for (SiteNode node : getContext(params).getNodesInContextFromSiteTree()) {
                    String uri = node.getHistoryReference().getURI().toString();
                    if (!addedUrls.contains(uri)) {
                        resultList.addItem(new ApiResponseElement("url", uri));
                        addedUrls.add(uri);
                    }
                }
                result = resultList;
                break;
            default:
                throw new ApiException(Type.BAD_VIEW);
        }
        return result;
    }

    /**
     * Returns the {@code Context} with the given name. The context's name is obtained from the
     * given parameters, whose name is {@value #CONTEXT_NAME}.
     *
     * 

The parameter must exist, that is, it should be a mandatory parameter, otherwise a runtime * exception is thrown. * * @param params the parameters that contain the context's name * @return the {@code Context} with the given name * @throws ApiException If the context with the given name does not exist * @see JSONObject#getString(String) */ private Context getContext(JSONObject params) throws ApiException { return ApiUtils.getContextByName(params, CONTEXT_NAME); } /** * Builds the response describing an Context. * * @param c the context * @return the api response */ private ApiResponse buildResponseFromContext(Context c) { Map fields = new HashMap<>(); fields.put("name", c.getName()); fields.put("id", Integer.toString(c.getId())); fields.put("description", c.getDescription()); fields.put("inScope", Boolean.toString(c.isInScope())); fields.put("excludeRegexs", jsonEncodeList(c.getExcludeFromContextRegexs())); fields.put("includeRegexs", jsonEncodeList(c.getIncludeInContextRegexs())); fields.put( "includedTechnologies", c.getTechSet().getIncludeTech().stream() .map(Tech::toString) .collect(Collectors.toList())); fields.put( "excludedTechnologies", c.getTechSet().getExcludeTech().stream() .map(Tech::toString) .collect(Collectors.toList())); AuthenticationMethod authenticationMethod = c.getAuthenticationMethod(); if (authenticationMethod != null) { Pattern pattern = authenticationMethod.getLoggedInIndicatorPattern(); fields.put("loggedInPattern", pattern == null ? "" : pattern.toString()); pattern = authenticationMethod.getLoggedOutIndicatorPattern(); fields.put("loggedOutPattern", pattern == null ? "" : pattern.toString()); AuthenticationMethodType type = authenticationMethod.getType(); fields.put("authType", type == null ? "" : type.getName()); AuthCheckingStrategy strategy = authenticationMethod.getAuthCheckingStrategy(); fields.put(PARAM_CHECKING_STRATEGRY, strategy == null ? "" : strategy.name()); if (AuthCheckingStrategy.POLL_URL.equals(strategy)) { fields.put(PARAM_POLL_URL, authenticationMethod.getPollUrl()); fields.put(PARAM_POLL_DATA, authenticationMethod.getPollData()); fields.put(PARAM_POLL_HEADERS, authenticationMethod.getPollData()); fields.put( PARAM_POLL_FREQ, Integer.toString(authenticationMethod.getPollFrequency())); AuthPollFrequencyUnits units = authenticationMethod.getPollFrequencyUnits(); fields.put(PARAM_POLL_FREQ_UNITS, units == null ? "" : units.name()); } } AuthorizationDetectionMethod authorizationDetectionMethod = c.getAuthorizationDetectionMethod(); if (authorizationDetectionMethod != null) { fields.put( "authenticationDetectionMethodId", String.valueOf(authorizationDetectionMethod.getMethodUniqueIdentifier())); } fields.put("urlParameterParserClass", c.getUrlParamParser().getClass().getCanonicalName()); fields.put("urlParameterParserConfig", c.getUrlParamParser().getConfig()); fields.put( "postParameterParserClass", c.getPostParamParser().getClass().getCanonicalName()); fields.put("postParameterParserConfig", c.getPostParamParser().getConfig()); return new ContextApiResponseSet<>("context", fields); } private static class ContextApiResponseSet extends ApiResponseSet { ContextApiResponseSet(String name, Map values) { super(name, values); } @Override public void toXML(Document doc, Element parent) { parent.setAttribute("type", "set"); for (Map.Entry val : getValues().entrySet()) { Element el = doc.createElement(val.getKey()); if ("includedTechnologies".equals(val.getKey()) || "excludedTechnologies".equals(val.getKey())) { el.setAttribute("type", "list"); @SuppressWarnings("unchecked") List techs = (List) val.getValue(); for (String tech : techs) { Element elTech = doc.createElement("tech"); elTech.appendChild( doc.createTextNode(XMLStringUtil.escapeControlChrs(tech))); el.appendChild(elTech); } } else { String textValue = val.getValue() == null ? "" : val.getValue().toString(); Text text = doc.createTextNode(XMLStringUtil.escapeControlChrs(textValue)); el.appendChild(text); } parent.appendChild(el); } } } private String jsonEncodeList(List list) { JSONArray js = new JSONArray(); for (String item : list) { js.add(item); } return js.toString(); } /** * Gets the tech that matches the techName or throws an exception if no tech matches * * @param techName the name of the tech * @return the matching tech * @throws ApiException the api exception */ private Tech getTech(String techName) throws ApiException { return Optional.ofNullable(Tech.get(techName)) .orElseThrow( () -> new ApiException( Type.ILLEGAL_PARAMETER, "The tech '" + techName + "' does not exist")); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy