# OPAMediator.java

    /*
     *  Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  WSO2 Inc. 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 org.apache.synapse.mediators.opa;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.ManagedLifecycle;
    import org.apache.synapse.MessageContext;
    import org.apache.synapse.SynapseException;
    import org.apache.synapse.core.SynapseEnvironment;
    import org.apache.synapse.mediators.AbstractMediator;

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.util.HashMap;
    import java.util.Map;

    public class OPAMediator extends AbstractMediator implements ManagedLifecycle {

        private static final Log log = LogFactory.getLog(OPAMediator.class);
        private static final String defaultRequestGenerator = "org.apache.synapse.mediators.opa.OPASynapseRequestGenerator";

        private String serverUrl = null;
        private String accessKey = null;
        private String policy = null;
        private String rule = null;
        private String requestGeneratorClassName = null;
        private Map<String, String> additionalParameters = new HashMap<String, String>();
        private OPARequestGenerator requestGenerator = null;
        private OPAClient opaClient;
        private String opaPolicyFailuretHandler = "_opa_policy_failure_handler_";

        @Override
        public boolean mediate(MessageContext messageContext) {

            try {
                String opaPayload = requestGenerator.generateRequest(policy, rule, additionalParameters, messageContext);

                String evaluatingPolicyUrl = serverUrl + "/" + policy;
                if (rule != null) {
                    evaluatingPolicyUrl += "/" + rule;
                }

                String opaResponseString = opaClient.publish(evaluatingPolicyUrl, opaPayload, accessKey);
                if (requestGenerator
                        .handleResponse(policy, rule, opaResponseString, additionalParameters, messageContext)) {
                    return true;
                } else {
                    if (log.isDebugEnabled()) {
                        log.debug("Access revoked for the API request by the OPA policy.OPA response " + opaResponseString);
                    }
                    log.warn("Access revoked for the API request by the OPA policy.");
                    throw new OPASecurityException(OPASecurityException.ACCESS_REVOKED,
                            OPASecurityException.ACCESS_REVOKED_MESSAGE);
                }
            } catch (OPASecurityException e) {
                OPAUtils.handlePolicyFailure(messageContext, e, opaPolicyFailuretHandler);
            }
            return false;
        }

        /**
         * Initialize the request generator object based on the className provided. If class name is not provided, default
         * policy generator object will be created
         *
         * @param className Request generator implementation
         * @return OPA request generator object
         * @throws OPASecurityException
         */
        private OPARequestGenerator getRequestGenerator(String className) throws OPASecurityException {

            try {
                if (className == null) {
                    className = defaultRequestGenerator;
                    if (log.isDebugEnabled()) {
                        log.debug("Request generator class not found. Default generator used.");
                    }
                }
                Class<?> requestGeneratorClassObject = Class.forName(className);
                Constructor<?> constructor = requestGeneratorClassObject.getConstructor();
                return (OPARequestGenerator) constructor.newInstance();
            } catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException
                    | InvocationTargetException e) {
                log.error("An error occurred while creating the request generator for " + className, e);
                throw new OPASecurityException(OPASecurityException.INTERNAL_ERROR,
                        OPASecurityException.INTERNAL_ERROR_MESSAGE);
            }
        }

        public String getServerUrl() {

            return serverUrl;
        }

        public void setServerUrl(String serverUrl) {

            this.serverUrl = serverUrl;
        }

        public String getAccessKey() {

            return accessKey;
        }

        public void setAccessKey(String accessKey) {

            this.accessKey = accessKey;
        }

        public String getRequestGeneratorClassName() {

            return requestGeneratorClassName;
        }

        public void setRequestGeneratorClassName(String requestGeneratorClassName) {

            this.requestGeneratorClassName = requestGeneratorClassName;
        }

        public String getPolicy() {

            return policy;
        }

        public void setPolicy(String policy) {

            this.policy = policy;
        }

        public String getRule() {

            return rule;
        }

        public void setRule(String rule) {

            this.rule = rule;
        }

        public Map<String, String> getAdditionalParameters() {

            return additionalParameters;
        }

        public void setAdditionalParameters(Map<String, String> additionalParameters) {

            this.additionalParameters = additionalParameters;
        }

        public void addAdditionalParameter(String parameter, String value) {

            this.additionalParameters.put(parameter, value);
        }

        @Override
        public void init(SynapseEnvironment se) {

            try {
                requestGenerator = getRequestGenerator(requestGeneratorClassName);
                opaClient = new OPAClient(serverUrl, additionalParameters);
                if (additionalParameters.get(OPAConstants.OPA_POLICY_FAILURE_HANDLER_PARAMETER) != null) {
                    opaPolicyFailuretHandler = additionalParameters.get(OPAConstants.OPA_POLICY_FAILURE_HANDLER_PARAMETER);
                }
            } catch (OPASecurityException e) {
                throw new SynapseException("Error when initializing the OPA Mediator", e);
            }
        }

        @Override
        public void destroy() {

        }
    }