Skip to content

Commit

Permalink
Add console role listener (#7416)
Browse files Browse the repository at this point in the history
add console role listener
  • Loading branch information
shashimalcse authored Jan 31, 2025
1 parent ff6ee39 commit 39e0596
Showing 6 changed files with 415 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/kind-eyes-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wso2is/identity-apps-core": patch
---

introduce console listener
Original file line number Diff line number Diff line change
@@ -88,6 +88,14 @@
<groupId>org.wso2.carbon.identity.organization.management</groupId>
<artifactId>org.wso2.carbon.identity.organization.management.application</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.api.resource.mgt</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.api.resource.collection.mgt</artifactId>
</dependency>
<!-- To fix travis build -->
<dependency>
<groupId>xalan</groupId>
@@ -141,7 +149,8 @@
org.wso2.carbon.identity.role.v2.mgt.core.*; version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.organization.management.service.*; version="${identity.org.mgt.core.version.range}",
org.wso2.carbon.identity.organization.management.application.*; version="${identity.org.mgt.application.version.range}",
org.wso2.carbon.identity.api.resource.mgt.*; version="${carbon.identity.framework.imp.pkg.version.range}"
org.wso2.carbon.identity.api.resource.mgt.*; version="${carbon.identity.framework.imp.pkg.version.range}",
org.wso2.carbon.identity.api.resource.collection.mgt.*; version="${carbon.identity.framework.imp.pkg.version.range}"
</import.package>
<dsannotations>*</dsannotations>
</properties>
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@

package org.wso2.identity.apps.common.internal;

import org.wso2.carbon.identity.api.resource.collection.mgt.APIResourceCollectionManager;
import org.wso2.carbon.identity.api.resource.mgt.APIResourceManager;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.oauth.OAuthAdminServiceImpl;
@@ -47,6 +48,7 @@ public class AppsCommonDataHolder {
private RoleManagementService roleManagementService;

private APIResourceManager apiResourceManager;
private APIResourceCollectionManager apiResourceCollectionManager;

private Set<String> systemAppConsumerKeys = new HashSet<>();

@@ -222,6 +224,26 @@ public APIResourceManager getAPIResourceManager() {
return apiResourceManager;
}

/**
* Set API resource collection manager.
*
* @param apiResourceCollectionManager APIResourceCollectionManager.
*/
public void setAPIResourceCollectionManager(APIResourceCollectionManager apiResourceCollectionManager) {

this.apiResourceCollectionManager = apiResourceCollectionManager;
}

/**
* Get API resource collection manager.
*
* @return apiResourceCollectionManager.
*/
public APIResourceCollectionManager getApiResourceCollectionManager() {

return apiResourceCollectionManager;
}


/**
* Set default applications.
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@
import org.osgi.service.component.annotations.ReferencePolicy;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.core.ServerStartupObserver;
import org.wso2.carbon.identity.api.resource.collection.mgt.APIResourceCollectionManager;
import org.wso2.carbon.identity.api.resource.mgt.APIResourceManager;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.InboundAuthenticationRequestConfig;
@@ -48,6 +49,7 @@
import org.wso2.identity.apps.common.listner.AppPortalOAuthAppMgtListener;
import org.wso2.identity.apps.common.listner.AppPortalRoleManagementListener;
import org.wso2.identity.apps.common.listner.AppPortalTenantMgtListener;
import org.wso2.identity.apps.common.listner.ConsoleRoleListener;
import org.wso2.identity.apps.common.util.AppPortalUtils;

import java.util.HashSet;
@@ -109,6 +111,10 @@ protected void activate(BundleContext bundleContext) {
RoleManagementListener roleManagementListener = new AppPortalRoleManagementListener(true);
bundleContext.registerService(RoleManagementListener.class.getName(), roleManagementListener, null);
log.debug("AppPortalRoleManagementListener registered successfully.");

RoleManagementListener consoleRoleListener = new ConsoleRoleListener();
bundleContext.registerService(RoleManagementListener.class.getName(), consoleRoleListener, null);
log.debug("ConsoleRoleListener registered successfully.");
}

if (!CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME) {
@@ -266,6 +272,22 @@ protected void unsetAPIResourceManager(APIResourceManager apiResourceManager) {
AppsCommonDataHolder.getInstance().setAPIResourceManager(null);
}

@Reference(
name = "api.resource.collection.mgt.service",
service = APIResourceCollectionManager.class,
cardinality = ReferenceCardinality.MANDATORY,
policy = ReferencePolicy.DYNAMIC,
unbind = "unsetAPIResourceCollectionManager")
protected void setAPIResourceManager(APIResourceCollectionManager apiResourceCollectionManager) {

AppsCommonDataHolder.getInstance().setAPIResourceCollectionManager(apiResourceCollectionManager);
}

protected void unsetAPIResourceCollectionManager(APIResourceCollectionManager apiResourceCollectionManager) {

AppsCommonDataHolder.getInstance().setAPIResourceCollectionManager(null);
}

private boolean skipPortalInitialization() {

return System.getProperty(SYSTEM_PROP_SKIP_SERVER_INITIALIZATION) != null;
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
/*
* Copyright (c) 2025, 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.identity.apps.common.listner;

import org.wso2.carbon.identity.api.resource.collection.mgt.exception.APIResourceCollectionMgtException;
import org.wso2.carbon.identity.api.resource.collection.mgt.model.APIResourceCollection;
import org.wso2.carbon.identity.api.resource.collection.mgt.model.APIResourceCollectionSearchResult;
import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo;
import org.wso2.carbon.identity.application.common.model.Scope;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants;
import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService;
import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException;
import org.wso2.carbon.identity.role.v2.mgt.core.listener.AbstractRoleManagementListener;
import org.wso2.carbon.identity.role.v2.mgt.core.model.Permission;
import org.wso2.carbon.identity.role.v2.mgt.core.model.Role;
import org.wso2.carbon.identity.role.v2.mgt.core.model.RoleBasicInfo;
import org.wso2.identity.apps.common.internal.AppsCommonDataHolder;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static org.wso2.carbon.identity.api.resource.collection.mgt.constant.APIResourceCollectionManagementConstants.APIResourceCollectionConfigBuilderConstants.EDIT_FEATURE_SCOPE_SUFFIX;
import static org.wso2.carbon.identity.api.resource.collection.mgt.constant.APIResourceCollectionManagementConstants.APIResourceCollectionConfigBuilderConstants.VIEW_FEATURE_SCOPE_SUFFIX;
import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.CONSOLE_APP_AUDIENCE_NAME;
import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.CONSOLE_ORG_SCOPE_PREFIX;
import static org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants.CONSOLE_SCOPE_PREFIX;

/**
* Console role listener to populate organization console application roles permissions.
*/
public class ConsoleRoleListener extends AbstractRoleManagementListener {

@Override
public int getDefaultOrderId() {

return 87;
}

@Override
public boolean isEnable() {

return true;
}

@Override
public void preAddRole(String roleName, List<String> userList, List<String> groupList, List<Permission> permissions,
String audience, String audienceId, String tenantDomain)
throws IdentityRoleManagementException {

if (isConsoleApp(audience, audienceId, tenantDomain) && !RoleConstants.ADMINISTRATOR.equals(roleName)) {
List<Permission> consoleFeaturePermissions = getConsoleFeaturePermissions(permissions);
if (consoleFeaturePermissions != null && !consoleFeaturePermissions.isEmpty()) {
// If console features are added to the role, then we need to we only need to persist the console
// permissions.
permissions.retainAll(consoleFeaturePermissions);
}
}
}

@Override
public void postGetRole(Role role, String roleId, String tenantDomain) throws IdentityRoleManagementException {


if (!RoleConstants.ADMINISTRATOR.equals(role.getName()) &&
role.getAudienceName().equals(CONSOLE_APP_AUDIENCE_NAME)) {
// Get updated console role permissions with newly added read and write scopes from API resource collection.
List<Permission> rolePermissions = getUpgradedPermissions(role.getPermissions(), tenantDomain);
role.setPermissions(rolePermissions);
}
}

@Override
public void postGetPermissionListOfRole(List<Permission> permissionListOfRole, String roleId, String tenantDomain)
throws IdentityRoleManagementException {

if (isConsoleRole(roleId, tenantDomain)) {
List<Permission> rolePermissions = getUpgradedPermissions(permissionListOfRole, tenantDomain);
permissionListOfRole.clear();
permissionListOfRole.addAll(rolePermissions);
}
}

@Override
public void postGetPermissionListOfRoles(List<String> permissions, List<String> roleIds, String tenantDomain)
throws IdentityRoleManagementException {

boolean isConsoleRole = false;
for (String roleId : roleIds) {
if (isConsoleRole(roleId, tenantDomain)) {
isConsoleRole = true;
break;
}
}
if (isConsoleRole) {
List<Permission> resolvedRolePermissions = new ArrayList<>();
List<Permission> systemPermissions = getSystemPermission(tenantDomain);
permissions.forEach(permission -> {
Optional<Permission> newPermission = systemPermissions.stream()
.filter(permission1 -> permission1.getName().equals(permission))
.findFirst();
newPermission.ifPresent(resolvedRolePermissions::add);
});
List<Permission> rolePermissions = getUpgradedPermissions(resolvedRolePermissions, tenantDomain);
permissions.clear();
permissions.addAll(rolePermissions.stream().map(Permission::getName).collect(Collectors.toList()));
}
}

@Override
public void preUpdatePermissionsForRole(String roleId, List<Permission> addedPermissions,
List<Permission> deletedPermissions, String audience, String audienceId,
String tenantDomain) throws IdentityRoleManagementException {

if (isConsoleRole(roleId, tenantDomain)) {
List<Permission> consoleFeaturePermissions = getConsoleFeaturePermissions(addedPermissions);
if (consoleFeaturePermissions != null && !consoleFeaturePermissions.isEmpty()) {
// If console features are added to the role, then we need to we only need to persist the console
// permissions.
addedPermissions.retainAll(consoleFeaturePermissions);
}
}
}

/**
* This method resolves the new permissions for the console roles. In this method, we resolve two type of console
* roles. 1. Console roles created after 7.0.0. 2. Console roles created in 7.0.0.
*
* @param rolePermissions List of permissions of the role.
* @param tenantDomain Tenant domain.
* @return List of resolved permissions.
* @throws IdentityRoleManagementException If an error occurs while resolving the permissions.
*/
private List<Permission> getUpgradedPermissions(List<Permission> rolePermissions, String tenantDomain)
throws IdentityRoleManagementException {

// Fetch all system scopes to resolve permission details from permission name.
List<Permission> systemPermissions = getSystemPermission(tenantDomain);
List<APIResourceCollection> apiResourceCollections = getAPIResourceCollections(tenantDomain);
List<Permission> consoleFeaturePermissions = getConsoleFeaturePermissions(rolePermissions);
if (!consoleFeaturePermissions.isEmpty()) {
// This is where we handle the new console roles (console roles created after 7.0.0) permissions.
// We check whether the role has the view feature scope or edit feature scope. If the role has the
// view feature scope, then we add all the read scopes. If the role has the edit feature scope, then we
// add all the write scopes.
List<Permission> resolvedRolePermissions = new ArrayList<>();
consoleFeaturePermissions.forEach(permission -> {
apiResourceCollections.forEach(apiResourceCollection -> {
// If the role has the edit feature scope, then we add all the write and read scopes.
if (apiResourceCollection.getEditFeatureScope() != null &&
apiResourceCollection.getEditFeatureScope().equals(permission.getName())) {
apiResourceCollection.getWriteScopes().forEach(writeScope -> {
Optional<Permission> newPermission = systemPermissions.stream()
.filter(permission1 -> permission1.getName().equals(writeScope))
.findFirst();
newPermission.ifPresent(resolvedRolePermissions::add);
});
}
if (apiResourceCollection.getViewFeatureScope() != null &&
apiResourceCollection.getViewFeatureScope().equals(permission.getName())) {
apiResourceCollection.getReadScopes().forEach(readScope -> {
Optional<Permission> newPermission = systemPermissions.stream()
.filter(permission1 -> permission1.getName().equals(readScope))
.findFirst();
newPermission.ifPresent(resolvedRolePermissions::add);
});
}
});
});
return resolvedRolePermissions;
} else {
// This is where we handle the initial console roles (console roles created in 7.0.0) permissions.
// Here we assume these role only contains legacy feature scope not the new feature scopes.
Set<Permission> resolvedRolePermissions = new HashSet<>(new ArrayList<>(rolePermissions));
List<Permission> consolePermissions = getConsolePermissions(rolePermissions);
consolePermissions.forEach(permission -> {
apiResourceCollections.forEach(apiResourceCollection -> {
// Match the permission with the collection.
if (apiResourceCollection.getReadScopes().contains(permission.getName())) {
// Add new read scopes since we have the feature scope.
apiResourceCollection.getReadScopes().forEach(newReadScope -> {
Optional<Permission> newPermission = systemPermissions.stream()
.filter(permission1 -> permission1.getName().equals(newReadScope))
.findFirst();
newPermission.ifPresent(resolvedRolePermissions::add);
});
List<String> legacyWriteScopes = apiResourceCollection.getLegacyWriteScopes();
// if all the writeScopes are in the role's permission list, then add new write scopes.
if (rolePermissions.stream().anyMatch(rolePermission ->
legacyWriteScopes.contains(rolePermission.getName()))) {
apiResourceCollection.getWriteScopes().forEach(newWriteScope -> {
Optional<Permission> newPermission = systemPermissions.stream()
.filter(permission1 -> permission1.getName().equals(newWriteScope))
.findFirst();
newPermission.ifPresent(resolvedRolePermissions::add);
});
}
}
});
});
return new ArrayList<>(resolvedRolePermissions);
}
}

/**
* Check whether the role is a console role. We consider all the console roles except the administrator role.
*
* @param roleId Role id.
* @param tenantDomain Tenant domain.
* @return True if the role is a console role.
* @throws IdentityRoleManagementException If an error occurs while checking the role.
*/
private boolean isConsoleRole(String roleId, String tenantDomain) throws IdentityRoleManagementException {

RoleManagementService roleManagementService = AppsCommonDataHolder.getInstance()
.getRoleManagementServiceV2();
RoleBasicInfo role = roleManagementService.getRoleBasicInfoById(roleId, tenantDomain);
return !RoleConstants.ADMINISTRATOR.equals(role.getName()) &&
role.getAudienceName().equals(CONSOLE_APP_AUDIENCE_NAME);
}

/**
* Check whether the app is a console application based in audience.
*
* @param audience Audience.
* @param audienceId Audience id.
* @param tenantDomain Tenant domain.
* @return True if the app is a console application.
* @throws IdentityRoleManagementException If an error occurs while checking the app.
*/
private boolean isConsoleApp(String audience, String audienceId, String tenantDomain)
throws IdentityRoleManagementException {

if (!RoleConstants.APPLICATION.equalsIgnoreCase(audience)) {
return false;
}
ApplicationManagementService applicationManagementService = AppsCommonDataHolder.getInstance()
.getApplicationManagementService();
try {
ApplicationBasicInfo applicationBasicInfo = applicationManagementService
.getApplicationBasicInfoByResourceId(audienceId, tenantDomain);
return applicationBasicInfo != null && CONSOLE_APP_AUDIENCE_NAME
.equals(applicationBasicInfo.getApplicationName());
} catch (IdentityApplicationManagementException e) {
throw new IdentityRoleManagementException("Error while retrieving application basic info for application " +
"id : " + audienceId, e);
}
}

/**
* Get API resource collections for the tenant. This will return all the tenant and organization specific API
* collections.
*
* @param tenantDomain Tenant domain.
* @return List of API resource collections.
* @throws IdentityRoleManagementException If an error occurs while retrieving the API resource collections.
*/
private List<APIResourceCollection> getAPIResourceCollections(String tenantDomain)
throws IdentityRoleManagementException {

try {
List<String> requiredAttributes = new ArrayList<>();
requiredAttributes.add("apiResources");
APIResourceCollectionSearchResult apiResourceCollectionSearchResult = AppsCommonDataHolder
.getInstance().getApiResourceCollectionManager()
.getAPIResourceCollections("", requiredAttributes, tenantDomain);
return apiResourceCollectionSearchResult.getAPIResourceCollections();

} catch (APIResourceCollectionMgtException e) {
throw new IdentityRoleManagementException("Error while retrieving api collection for tenant : " +
tenantDomain, e);
}
}

/**
* Get console feature permissions from the role permissions.
*
* @param rolePermissions Role permissions.
* @return List of console feature permissions.
*/
private List<Permission> getConsoleFeaturePermissions(List<Permission> rolePermissions) {

return rolePermissions.stream().filter(permission -> permission != null &&
permission.getName() != null && (permission.getName().startsWith(CONSOLE_SCOPE_PREFIX)
|| permission.getName().startsWith(CONSOLE_ORG_SCOPE_PREFIX)) &&
(permission.getName().endsWith(VIEW_FEATURE_SCOPE_SUFFIX) ||
permission.getName().endsWith(EDIT_FEATURE_SCOPE_SUFFIX)))
.collect(Collectors.toList());
}

/**
* Get console permissions (old ones) from the role permissions.
*
* @param rolePermissions Role permissions.
* @return List of console permissions.
*/
private List<Permission> getConsolePermissions(List<Permission> rolePermissions) {

return rolePermissions.stream().filter(permission -> permission != null &&
permission.getName() != null && (permission.getName().startsWith(CONSOLE_SCOPE_PREFIX)
|| permission.getName().startsWith(CONSOLE_ORG_SCOPE_PREFIX)) &&
!(permission.getName().endsWith(VIEW_FEATURE_SCOPE_SUFFIX) ||
permission.getName().endsWith(EDIT_FEATURE_SCOPE_SUFFIX)))
.collect(Collectors.toList());
}

/**
* Get system permissions for the tenant.
*
* @param tenantDomain Tenant domain.
* @return List of system permissions.
* @throws IdentityRoleManagementException If an error occurs while retrieving the system permissions.
*/
private List<Permission> getSystemPermission(String tenantDomain) throws IdentityRoleManagementException {
List<Scope> systemScopes;

try {
systemScopes = AppsCommonDataHolder.getInstance()
.getAPIResourceManager().getSystemAPIScopes(tenantDomain);
} catch (APIResourceMgtException e) {
throw new IdentityRoleManagementException("Error while retrieving internal scopes for tenant " +
"domain : " + tenantDomain, e);
}
return systemScopes.stream().map(scope -> new Permission(scope.getName(), scope.getDisplayName(),
scope.getApiID())).collect(Collectors.toList());
}
}

7 changes: 6 additions & 1 deletion identity-apps-core/pom.xml
Original file line number Diff line number Diff line change
@@ -401,6 +401,11 @@
<artifactId>org.wso2.carbon.identity.api.resource.mgt</artifactId>
<version>${carbon.identity.framework.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.api.resource.collection.mgt</artifactId>
<version>${carbon.identity.framework.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.governance</groupId>
<artifactId>org.wso2.carbon.identity.captcha</artifactId>
@@ -729,7 +734,7 @@
<carbon.extension.identity.authenticator.version>3.0.0</carbon.extension.identity.authenticator.version>
<org.wso2.carbon.identity.association.account>5.1.5</org.wso2.carbon.identity.association.account>
<identity.extension.utils>1.0.8</identity.extension.utils>
<carbon.identity.framework.version>7.2.30</carbon.identity.framework.version>
<carbon.identity.framework.version>7.7.176</carbon.identity.framework.version>
<carbon.identity.framework.imp.pkg.version.range>[5.0.0, 8.0.0)</carbon.identity.framework.imp.pkg.version.range>
<identity.organization.management.core.service.version>1.0.77
</identity.organization.management.core.service.version>

0 comments on commit 39e0596

Please sign in to comment.