Skip to content

Commit

Permalink
Merge pull request #12715 from Lakith-Rambukkanage/master
Browse files Browse the repository at this point in the history
Add Basic Auth Blocked URI configuration to RestAPIs
  • Loading branch information
Lakith-Rambukkanage authored Dec 13, 2024
2 parents c298b5e + 1e4ecca commit c644562
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,10 @@ private Permissions() {
public static final String API_RESTAPI_ALLOWED_URI = API_RESTAPI + "AllowedURIs.AllowedURI.";
public static final String API_RESTAPI_ALLOWED_URI_URI = API_RESTAPI_ALLOWED_URI + "URI";
public static final String API_RESTAPI_ALLOWED_URI_HTTPMethods = API_RESTAPI_ALLOWED_URI + "HTTPMethods";
public static final String API_RESTAPI_BASIC_AUTH_BLOCKED_URI = API_RESTAPI + "BasicAuthBlockedURIs.BasicAuthBlockedURI.";
public static final String API_RESTAPI_BASIC_AUTH_BLOCKED_URI_URI = API_RESTAPI_BASIC_AUTH_BLOCKED_URI + "URI";
public static final String API_RESTAPI_BASIC_AUTH_BLOCKED_URI_HTTPMethods =
API_RESTAPI_BASIC_AUTH_BLOCKED_URI + "HTTPMethods";
public static final String API_RESTAPI_ETAG_SKIP_LIST = API_RESTAPI + "ETagSkipList.";
public static final String API_RESTAPI_ETAG_SKIP_URI = API_RESTAPI_ETAG_SKIP_LIST + "ETagSkipURI.";
public static final String API_RESTAPI_ETAG_SKIP_URI_URI = API_RESTAPI_ETAG_SKIP_URI + "URI";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.wso2.carbon.apimgt.api.APIManagementException;
import org.wso2.carbon.apimgt.api.model.Scope;
import org.wso2.carbon.apimgt.api.model.URITemplate;
import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.utils.APIUtil;
import org.wso2.carbon.apimgt.impl.utils.RealmUtil;
import org.wso2.carbon.apimgt.rest.api.common.RestApiCommonUtil;
Expand All @@ -46,6 +48,8 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -87,6 +91,14 @@ public void handleMessage(Message inMessage) {
if (policy != null) {
inMessage.put(RestApiConstants.REQUEST_AUTHENTICATION_SCHEME, RestApiConstants.BASIC_AUTHENTICATION);
//Extract user credentials from the auth header and validate.
String path = (String) inMessage.get(Message.PATH_INFO);
String httpMethod = (String) inMessage.get(Message.HTTP_REQUEST_METHOD);
if (isBasicAuthBlockedURI(path, httpMethod)) {
log.error("Requested URI:" + path + " with HTTP method: " + httpMethod +
" is not allowed with Basic Authentication");
throw new AuthenticationException("Unauthenticated request");
}

String username = StringUtils.trim(policy.getUserName());
String password = StringUtils.trim(policy.getPassword());
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
Expand Down Expand Up @@ -306,4 +318,40 @@ private boolean validateUserRolesWithRESTAPIScopes(List<Scope> resourceScopeList
return false;
}

/**
* This method will check if the requested URI is allowed to access with Basic Authentication
*
* @param path Requested URI path
* @param httpMethod HTTP Method
* @return true if the requested URI is not allowed with Basic Authentication
*/
private boolean isBasicAuthBlockedURI(String path, String httpMethod) {
Dictionary<org.wso2.uri.template.URITemplate,List<String>> blockedResourcePathsMap;
if (path.contains(APIConstants.RestApiConstants.REST_API_OLD_VERSION)) {
path = path.replace("/" + APIConstants.RestApiConstants.REST_API_OLD_VERSION, "");
}

//Check if the accessing URI is Basic Auth allowed and then authorization is failed if not.
try {
blockedResourcePathsMap = RestApiUtil.getBasicAuthBlockedURIsToMethodsMap();
Enumeration<org.wso2.uri.template.URITemplate> uriTemplateSet = blockedResourcePathsMap.keys();

while (uriTemplateSet.hasMoreElements()) {
org.wso2.uri.template.URITemplate uriTemplate = uriTemplateSet.nextElement();
if (uriTemplate.matches(path, new HashMap<String, String>())) {
List<String> blockedVerbs = blockedResourcePathsMap.get(uriTemplate);
if (blockedVerbs.contains(httpMethod)) {
return true;
}
}
}

return false;
} catch (APIManagementException e) {
RestApiUtil
.handleInternalServerError("Unable to retrieve/process " +
"Basic Auth blocked URIs for REST API", e, log);
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class RestApiUtil {

public static final Log log = LogFactory.getLog(RestApiUtil.class);
private static Dictionary<org.wso2.uri.template.URITemplate, List<String>> uriToHttpMethodsMap;
private static Dictionary<org.wso2.uri.template.URITemplate, List<String>> basicAuthBlockedUriToHttpMethodsMap;
private static Dictionary<org.wso2.uri.template.URITemplate, List<String>> ETagSkipListURIToHttpMethodsMap;

public static <T> ErrorDTO getConstraintViolationErrorDTO(Set<ConstraintViolation<T>> violations) {
Expand Down Expand Up @@ -1094,6 +1095,67 @@ public static Dictionary<org.wso2.uri.template.URITemplate, List<String>> getAll
return uriToHttpMethodsMap;
}

/**
* Returns the Basic Auth Blocked URIs and associated HTTP methods for REST API
* by reading api-manager.xml configuration
*
* @return A Dictionary with the Basic Auth Blocked URIs and the associated HTTP methods.
* @throws APIManagementException
*/
private static Dictionary<org.wso2.uri.template.URITemplate, List<String>> getBasicAuthBlockedURIsMapFromConfig()
throws APIManagementException {
Dictionary<org.wso2.uri.template.URITemplate, List<String>> uriToMethodsMap = new Hashtable<>();
APIManagerConfiguration apiManagerConfiguration = ServiceReferenceHolder.getInstance()
.getAPIManagerConfigurationService().getAPIManagerConfiguration();
List<String> uriList = apiManagerConfiguration
.getProperty(APIConstants.API_RESTAPI_BASIC_AUTH_BLOCKED_URI_URI);
List<String> methodsList = apiManagerConfiguration
.getProperty(APIConstants.API_RESTAPI_BASIC_AUTH_BLOCKED_URI_HTTPMethods);

if (uriList != null && methodsList != null) {
if (uriList.size() != methodsList.size()) {
String errorMsg = "Provided Basic Auth Blocked URIs for REST API are invalid."
+ " Every 'BasicAuthAllowedURI' should include 'URI' and 'HTTPMethods' elements";
log.error(errorMsg);
return new Hashtable<>();
}

for (int i = 0; i < uriList.size(); i++) {
String uri = uriList.get(i);
uri = uri.replace("/{version}", "");
try {
org.wso2.uri.template.URITemplate uriTemplate = new org.wso2.uri.template.URITemplate(uri);
String methodsForUri = methodsList.get(i);
List<String> methodListForUri = Arrays.asList(methodsForUri.split(","));
uriToMethodsMap.put(uriTemplate, methodListForUri);
} catch (URITemplateException e) {
String msg = "Error in parsing URI " + uri
+ " when retrieving Basic Auth Blocked URIs for REST API";
log.error(msg, e);
throw new APIManagementException(msg, e);
}
}
}
return uriToMethodsMap;
}

/**
* Returns the Basic Auth Blocked URIs and associated HTTP methods for REST API. If not already read before, reads
* api-manager.xml configuration, store the results in a static reference and returns the results.
* Otherwise, returns previously stored the static reference object.
*
* @return A Dictionary with the Basic Auth Allowed URIs and the associated HTTP methods.
* @throws APIManagementException
*/
public static Dictionary<org.wso2.uri.template.URITemplate, List<String>> getBasicAuthBlockedURIsToMethodsMap()
throws APIManagementException {

if (basicAuthBlockedUriToHttpMethodsMap == null) {
basicAuthBlockedUriToHttpMethodsMap = getBasicAuthBlockedURIsMapFromConfig();
}
return basicAuthBlockedUriToHttpMethodsMap;
}

/**
* @param message CXF message to be extract auth header
* @param pattern Pattern to extract access token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,17 @@
<HTTPMethods>POST</HTTPMethods>
</AllowedURI>
</AllowedURIs>
<BasicAuthBlockedURIs>
<!--Configure Basic Auth blocked URIs of REST API. URIs included cannot be invoked with basic auth-->
{% if apim.rest_api.basic_auth_blocked_uri is defined %}
{% for uri in apim.rest_api.basic_auth_blocked_uri %}
<BasicAuthBlockedURI>
<URI>{{uri.uri_path}}</URI>
<HTTPMethods>{{uri.http_method}}</HTTPMethods>
</BasicAuthBlockedURI>
{% endfor %}
{% endif %}
</BasicAuthBlockedURIs>
<ETagSkipList>
<ETagSkipURI>
<URI>/api/am/devportal/{version}/apis</URI>
Expand Down

0 comments on commit c644562

Please sign in to comment.