Skip to content
Draft
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
Expand Up @@ -18,7 +18,9 @@
package org.wso2.carbon.identity.auth.service.module;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Model class to hold resource access configs.
Expand All @@ -34,6 +36,7 @@ public class ResourceConfig implements Serializable {
// Comma separated list of allowed authentication handler names. If all handlers are engaged the value is 'all'
private String allowedAuthHandlers;
private List<String> crossAccessAllowedTenants;
private Map<String, String> operationScopeMap = new HashMap<>();

public String getContext() {
return context;
Expand Down Expand Up @@ -104,4 +107,14 @@ public void setScopes(List<String> scopes) {

this.scopes = scopes;
}

public Map<String, String> getOperationScopeMap() {

return operationScopeMap;
}

public void setOperationScopeMap(Map<String, String> operationScopeMap) {

this.operationScopeMap = operationScopeMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ public static AuthConfigurationUtil getInstance() {

public ResourceConfig getSecuredConfig(ResourceConfigKey resourceConfigKey) {
ResourceConfig resourceConfig = null;
for ( Map.Entry<ResourceConfigKey, ResourceConfig> entry : resourceConfigMap.entrySet() ) {
if ( entry.getKey().equals(resourceConfigKey) ) {
for (Map.Entry<ResourceConfigKey, ResourceConfig> entry : resourceConfigMap.entrySet()) {
if (entry.getKey().equals(resourceConfigKey)) {
resourceConfig = entry.getValue();
break;
}
Expand All @@ -110,15 +110,15 @@ public ResourceConfig getSecuredConfig(ResourceConfigKey resourceConfigKey) {
public void buildResourceAccessControlData() {

OMElement resourceAccessControl = getResourceAccessControlConfigs();
if ( resourceAccessControl != null ) {
if (resourceAccessControl != null) {
defaultAccess = resourceAccessControl.getAttributeValue(new QName(Constants.RESOURCE_DEFAULT_ACCESS));
isScopeValidationEnabled = !Boolean.parseBoolean(resourceAccessControl
.getAttributeValue(new QName(Constants.RESOURCE_DISABLE_SCOPE_VALIDATION)));
Iterator<OMElement> resources = resourceAccessControl.getChildrenWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, Constants.RESOURCE_ELE));
if ( resources != null ) {

while ( resources.hasNext() ) {
while (resources.hasNext()) {
OMElement resource = resources.next();
ResourceConfig resourceConfig = new ResourceConfig();
String httpMethod = resource.getAttributeValue(
Expand Down Expand Up @@ -183,6 +183,33 @@ public void buildResourceAccessControlData() {
resourceConfig.setAllowedAuthHandlers(allowedAuthHandlers);
resourceConfig.setPermissions(permissionBuilder.toString());
resourceConfig.setScopes(scopes);

// Parse <Operations> if present
Iterator<OMElement> operationsElementItr = resource.getChildrenWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, "Operations"));

if (operationsElementItr != null && operationsElementItr.hasNext()) {
OMElement operationsElement = operationsElementItr.next(); // There should be only one <Operations> per <Resource>
Iterator<OMElement> operationElements = operationsElement.getChildrenWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, "Operation"));

Map<String, String> operationScopeMap = new HashMap<>();
while (operationElements.hasNext()) {
OMElement operationElement = operationElements.next();
String operationName = operationElement.getAttributeValue(new QName("name"));
OMElement scopeElement = operationElement.getFirstChildWithName(
new QName(IdentityCoreConstants.IDENTITY_DEFAULT_NAMESPACE, "scope"));

if (StringUtils.isNotBlank(operationName) && scopeElement != null &&
StringUtils.isNotBlank(scopeElement.getText())) {
operationScopeMap.put(operationName, scopeElement.getText());
}
}
if (!operationScopeMap.isEmpty()) {
resourceConfig.setOperationScopeMap(operationScopeMap);
}
}

ResourceConfigKey resourceConfigKey = new ResourceConfigKey(context, httpMethod);
if (!resourceConfigMap.containsKey(resourceConfigKey)) {
resourceConfigMap.put(resourceConfigKey, resourceConfig);
Expand Down Expand Up @@ -426,6 +453,7 @@ public static boolean isAuthHeaderMatch(MessageContext messageContext, String au
}
return false;
}

/**
* Build configurations of endpoints which are allowed to skip authorization with particular auth handler.
*/
Expand Down Expand Up @@ -482,4 +510,47 @@ public static String getResourceResidentTenantForTenantPerspective(String reques
}
return null;
}

/**
* Retrieve the scope associated with a specific operation by searching through all resource configurations.
*
* @param operationName The name of the operation to lookup.
* @return The scope associated with the operation, or null if not found.
* @throws IllegalArgumentException if operationName is null or empty.
*/
public String getScopeForOperation(String operationName) {

if (StringUtils.isBlank(operationName)) {
throw new IllegalArgumentException("Operation name cannot be null or empty");
}

if (resourceConfigMap == null || resourceConfigMap.isEmpty()) {
log.debug("No resource configurations available to search for operation: " + operationName);
return null;
}

for (ResourceConfig resourceConfig : resourceConfigMap.values()) {
if (resourceConfig == null) {
continue;
}

Map<String, String> operationScopeMap = resourceConfig.getOperationScopeMap();
if (operationScopeMap == null || operationScopeMap.isEmpty()) {
continue;
}

String scope = operationScopeMap.get(operationName);
if (scope != null) {
if (log.isDebugEnabled()) {
log.debug("Found scope '" + scope + "' for operation '" + operationName + "'");
}
return scope;
}
}

log.debug("No scope found for operation: " + operationName);
return null;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public class Constants {
public final static String RESOURCE_ALLOWED_AUTH_HANDLERS = "allowed-auth-handlers";
public final static String RESOURCE_ALLOWED_AUTH_HANDLERS_ALL = "all";

public final static String AND = "AND";
public final static String OR = "OR";

public final static String CLIENT_APP_AUTHENTICATION_ELE = "ClientAppAuthentication";
public final static String APPLICATION_NAME_ATTR = "name";
public final static String APPLICATION_HASH_ATTR = "hash";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import org.wso2.carbon.identity.core.bean.context.MessageContext;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AuthorizationContext extends MessageContext {

Expand All @@ -35,7 +37,7 @@ public class AuthorizationContext extends MessageContext {
private boolean isCrossTenantAllowed;
private String tenantDomainFromURLMapping;
private List<String> allowedTenants;

private Map<String, String> operationScopeMap = new HashMap<>();

public User getUser() {
return user;
Expand Down Expand Up @@ -107,4 +109,14 @@ public void setRequiredScopes(List<String> requiredScopes) {

this.requiredScopes = requiredScopes;
}

public Map<String, String> getOperationScopeMap() {

return operationScopeMap;
}

public void setOperationScopeMap(Map<String, String> operationScopeMap) {

this.operationScopeMap = operationScopeMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static org.wso2.carbon.identity.auth.service.util.Constants.OAUTH2_ALLOWED_SCOPES;
import static org.wso2.carbon.identity.auth.service.util.Constants.OAUTH2_VALIDATE_SCOPE;
import static org.wso2.carbon.identity.auth.service.util.Constants.RESOURCE_ORGANIZATION_ID;
Expand Down Expand Up @@ -171,14 +176,28 @@ private void validatePermissions(AuthorizationResult authorizationResult, User u
private void validateScopes(AuthorizationContext authorizationContext, AuthorizationResult authorizationResult, String[] allowedScopes) {

boolean granted = true;
boolean operationScopesGranted = false;

if (allowedScopes != null) {
for (String scope : authorizationContext.getRequiredScopes()) {
if (!ArrayUtils.contains(allowedScopes, scope)) {
granted = false;
break;
}
}
if (granted) {

// Check if at least one operation scope is satisfied
Map<String, String> operationScopeMap = authorizationContext.getOperationScopeMap();
if (operationScopeMap != null && !operationScopeMap.isEmpty()) {
for (String opScope : operationScopeMap.values()) {
if (ArrayUtils.contains(allowedScopes, opScope)) {
operationScopesGranted = true;
break;
}
}
}

if (granted || operationScopesGranted) {
authorizationResult.setAuthorizationStatus(AuthorizationStatus.GRANT);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -114,6 +116,11 @@ public void invoke(Request request, Response response) throws IOException, Servl
if (resourceConfig != null && CollectionUtils.isNotEmpty(resourceConfig.getScopes())) {
authorizationContext.setRequiredScopes(resourceConfig.getScopes());
}
if (resourceConfig != null && resourceConfig.getOperationScopeMap() != null) {
Map<String, String> operationScopeMap = new HashMap<>();
operationScopeMap = resourceConfig.getOperationScopeMap();
authorizationContext.setOperationScopeMap(operationScopeMap);
}
String contextPath = request.getContextPath();
String httpMethod = request.getMethod();
authorizationContext.setContext(contextPath);
Expand All @@ -140,6 +147,9 @@ public void invoke(Request request, Response response) throws IOException, Servl
try {
AuthorizationResult authorizationResult = authorizationManager.authorize(authorizationContext);
if (authorizationResult.getAuthorizationStatus().equals(AuthorizationStatus.GRANT)) {
String[] allowedScopes = authorizationContext.getParameter(OAUTH2_ALLOWED_SCOPES) == null ? null :
(String[]) authorizationContext.getParameter(OAUTH2_ALLOWED_SCOPES);
PrivilegedCarbonContext.getThreadLocalCarbonContext().setAllowedScopes(List.of(allowedScopes));
if (authorizationContext.getUser() instanceof AuthenticatedUser) {
String authorizedOrganization = ((AuthenticatedUser)authorizationContext.getUser())
.getAccessingOrganization();
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@
<identity.framework.version>7.8.123</identity.framework.version>
<carbon.identity.package.import.version.range>[5.17.8, 8.0.0)</carbon.identity.package.import.version.range>

<org.wso2.carbon.identity.oauth.version>7.0.305</org.wso2.carbon.identity.oauth.version>
<org.wso2.carbon.identity.oauth.version>7.0.318</org.wso2.carbon.identity.oauth.version>
<org.wso2.carbon.identity.oauth.import.version.range>[6.2.18, 8.0.0)
</org.wso2.carbon.identity.oauth.import.version.range>

Expand All @@ -463,7 +463,7 @@
<osgi.util.tracker.imp.pkg.version.range>[1.5.1, 2.0.0)</osgi.util.tracker.imp.pkg.version.range>

<!-- Carbon Kernel version -->
<carbon.kernel.version>4.10.43</carbon.kernel.version>
<carbon.kernel.version>4.10.68</carbon.kernel.version>
<carbon.kernel.feature.version>4.9.0</carbon.kernel.feature.version>
<carbon.kernel.imp.pkg.version.range>[4.5.0, 5.0.0)</carbon.kernel.imp.pkg.version.range>

Expand Down