diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/RestAPI.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/RestAPI.java index c220107b4..80beea602 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/RestAPI.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/api/RestAPI.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -44,7 +44,7 @@ import org.wso2.apk.enforcer.util.EndpointUtils; import org.wso2.apk.enforcer.util.FilterUtils; import org.wso2.apk.enforcer.util.MockImplUtils; - +import org.wso2.apk.enforcer.deny.DenyFilter; import java.security.KeyStore; import java.security.KeyStoreException; import java.util.ArrayList; @@ -234,12 +234,17 @@ private void initFilters() { // CORS filter is added as the first filter, and it is not customizable. CorsFilter corsFilter = new CorsFilter(); this.filters.add(0, corsFilter); + + DenyFilter denyFilter = new DenyFilter(); + denyFilter.init(apiConfig, null); + this.filters.add(denyFilter); } private void loadCustomFilters(APIConfig apiConfig) { FilterDTO[] customFilters = ConfigHolder.getInstance().getConfig().getCustomFilters(); - // Needs to sort the filter in ascending order to position the filter in the given position. + // Needs to sort the filter in ascending order to position the filter in the + // given position. Arrays.sort(customFilters, Comparator.comparing(FilterDTO::getPosition)); Map filterImplMap = new HashMap<>(customFilters.length); ServiceLoader loader = ServiceLoader.load(Filter.class); diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java index 978bfb18b..bb8bbf885 100644 --- a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/constants/APIConstants.java @@ -65,11 +65,13 @@ public class APIConstants { public static final String NOT_FOUND_MESSAGE = "Not Found"; public static final String NOT_FOUND_DESCRIPTION = "The requested resource is not available."; + public static final String REQUEST_DENIED_MESSAGE = "Unauthorized"; + public static final String REQUEST_DENIED_DESCRIPTION = "You have been blocked from accessing this resource."; public static final String NOT_IMPLEMENTED_MESSAGE = "Not Implemented"; public static final String BAD_REQUEST_MESSAGE = "Bad Request"; public static final String INTERNAL_SERVER_ERROR_MESSAGE = "Internal Server Error"; - //headers and values + // headers and values public static final String CONTENT_TYPE_HEADER = "content-type"; public static final String SOAP_ACTION_HEADER_NAME = "soapaction"; public static final String ACCEPT_HEADER = "accept"; @@ -105,7 +107,8 @@ public static class ErrorResponseTypes { } /** - * Holds the common set of constants related to the output status codes of the security validations. + * Holds the common set of constants related to the output status codes of the + * security validations. */ public static class KeyValidationStatus { @@ -187,7 +190,8 @@ public static class KeyManager { public static final String APIM_PUBLISHER_ISSUER = "APIM Publisher"; public static final String APIM_APIKEY_ISSUER = "APIM APIkey"; - // APIM_APIKEY_ISSUER_URL is intentionally different from the Resident Key Manager + // APIM_APIKEY_ISSUER_URL is intentionally different from the Resident Key + // Manager // to avoid conflicts with the access token issuer configs. public static final String APIM_APIKEY_ISSUER_URL = "https://host:9443/apikey"; @@ -212,7 +216,8 @@ public enum PolicyType { } /** - * Holds the constants related to attributes to be sent in the response in case of an error + * Holds the constants related to attributes to be sent in the response in case + * of an error * scenario raised within the enforcer. */ public static class MessageFormat { diff --git a/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/deny/DenyFilter.java b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/deny/DenyFilter.java new file mode 100644 index 000000000..baa6e0a0c --- /dev/null +++ b/gateway/enforcer/org.wso2.apk.enforcer/src/main/java/org/wso2/apk/enforcer/deny/DenyFilter.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. 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.wso2.apk.enforcer.deny; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.wso2.apk.enforcer.commons.Filter; +import org.wso2.apk.enforcer.commons.model.APIConfig; +import org.wso2.apk.enforcer.commons.model.RequestContext; +import org.wso2.apk.enforcer.constants.APIConstants; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * enum for storing types of deny policies + */ +enum DenyPolicyType { + APPLICATION, SUBSCRIPTION, USER +} + +public class DenyFilter implements Filter { + + private static final Logger logger = LogManager.getLogger(DenyFilter.class); + + // Hashmap to keep track of the blocked subs, apps and users. + private final HashMap> deniedDetailsMap = new HashMap<>(); + + @Override + public void init(APIConfig apiConfig, Map configProperties) { + Filter.super.init(apiConfig, configProperties); + loadDeniedList(); + } + + /** + * + * @param requestContext {@code RequestContext} object + * @return boolean + */ + @Override + public boolean handleRequest(RequestContext requestContext) { + String username = requestContext.getAuthenticationContext().getUsername(); + String applicationId = requestContext.getAuthenticationContext().getApplicationUUID(); + String subscriptionId = requestContext.getAuthenticationContext().getSubscriber(); + + if (isInDeniedList(username, DenyPolicyType.USER) || isInDeniedList(applicationId, DenyPolicyType.APPLICATION) || + isInDeniedList(subscriptionId, DenyPolicyType.SUBSCRIPTION)) { + logger.debug("Request blocked due to deny policy (enforcer)."); + requestContext.getProperties() + .put(APIConstants.MessageFormat.STATUS_CODE, APIConstants.StatusCodes.UNAUTHORIZED.getCode()); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_CODE, APIConstants.StatusCodes.UNAUTHORIZED.getValue()); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_MESSAGE, APIConstants.REQUEST_DENIED_MESSAGE); + requestContext.getProperties().put(APIConstants.MessageFormat.ERROR_DESCRIPTION, APIConstants.REQUEST_DENIED_DESCRIPTION); + return false; + } + return true; + } + + private void loadDeniedList() { + // TODO (Gayangi): implement loading data from the database once database is implemented + deniedDetailsMap.put(DenyPolicyType.USER, new ArrayList<>()); + deniedDetailsMap.put(DenyPolicyType.APPLICATION, new ArrayList<>()); + deniedDetailsMap.put(DenyPolicyType.SUBSCRIPTION, new ArrayList<>()); + } + + /** + * + * @param value Represents the value to be checked to see if it has been blocked + * @param denyPolicyType + * @return true if value is in the relevant denied list + */ + private boolean isInDeniedList(String value, DenyPolicyType denyPolicyType) { + return deniedDetailsMap.get(denyPolicyType).contains(value); + } +}