Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deny policy implementation in enforcer #1832

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, Filter> filterImplMap = new HashMap<>(customFilters.length);
ServiceLoader<Filter> loader = ServiceLoader.load(Filter.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 {

Expand Down Expand Up @@ -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";

Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<DenyPolicyType, ArrayList<String>> deniedDetailsMap = new HashMap<>();

@Override
public void init(APIConfig apiConfig, Map<String, String> 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);
}
}
Loading