Skip to content

Commit

Permalink
Introduce the fragment app check when adding the app role association
Browse files Browse the repository at this point in the history
  • Loading branch information
ShanChathusanda93 committed Dec 13, 2024
1 parent 37745ca commit f744960
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo;
import org.wso2.carbon.identity.application.common.model.AuthorizedScopes;
import org.wso2.carbon.identity.application.common.model.ServiceProvider;
import org.wso2.carbon.identity.application.mgt.ApplicationConstants;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementService;
import org.wso2.carbon.identity.application.mgt.AuthorizedAPIManagementServiceImpl;
Expand All @@ -34,6 +35,7 @@
import org.wso2.carbon.identity.application.mgt.internal.cache.ServiceProviderByResourceIdCache;
import org.wso2.carbon.identity.application.mgt.internal.cache.ServiceProviderIDCacheKey;
import org.wso2.carbon.identity.application.mgt.internal.cache.ServiceProviderResourceIdCacheKey;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementClientException;
import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException;
import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementServerException;
Expand Down Expand Up @@ -92,7 +94,7 @@ public void preAddRole(String roleName, List<String> userList, List<String> grou
throws IdentityRoleManagementException {

if (APPLICATION.equalsIgnoreCase(audience)) {
validateApplicationRoleAudience(audienceId, tenantDomain);
validateApplicationTypeAndRoleAudience(audienceId, tenantDomain);
validatePermissionsForApplication(permissions, audienceId, tenantDomain);
}
}
Expand Down Expand Up @@ -519,7 +521,7 @@ public void postGetAssociatedApplicationIdsByRoleId(List<String> associatedAppli
* @param tenantDomain Tenant domain.
* @throws IdentityRoleManagementException Error occurred while validating application role audience.
*/
private void validateApplicationRoleAudience(String applicationId, String tenantDomain)
private void validateApplicationTypeAndRoleAudience(String applicationId, String tenantDomain)
throws IdentityRoleManagementException {

try {
Expand All @@ -536,6 +538,17 @@ private void validateApplicationRoleAudience(String applicationId, String tenant
throw new IdentityRoleManagementClientException(INVALID_AUDIENCE.getCode(),
"Application: " + applicationId + " does not have Application role audience type");
}

// Set thread local property to identify that the application is a fragment application. This property
// will be used in the role management component to identify the application type.
if (app.getSpProperties() != null && Arrays.stream(app.getSpProperties())
.anyMatch(property -> ApplicationConstants.IS_FRAGMENT_APP.equals(property.getName())
&& Boolean.parseBoolean(property.getValue()))) {
if (IdentityUtil.threadLocalProperties.get().get(ApplicationConstants.IS_FRAGMENT_APP) != null) {
IdentityUtil.threadLocalProperties.get().remove(ApplicationConstants.IS_FRAGMENT_APP);
}
IdentityUtil.threadLocalProperties.get().put(ApplicationConstants.IS_FRAGMENT_APP, Boolean.TRUE);
}
} catch (IdentityApplicationManagementException e) {
String errorMessage = "Error while retrieving the application for the given id: " + applicationId;
throw new IdentityRoleManagementServerException(UNEXPECTED_SERVER_ERROR.getCode(), errorMessage, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,70 +75,78 @@ public class RoleManagementServiceImpl implements RoleManagementService {
private static final Log log = LogFactory.getLog(RoleManagementServiceImpl.class);
private final RoleDAO roleDAO = RoleMgtDAOFactory.getInstance().getRoleDAO();
private final UserIDResolver userIDResolver = new UserIDResolver();
private static final String IS_FRAGMENT_APP = "isFragmentApp";

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

if (StringUtils.startsWithIgnoreCase(roleName, UserCoreConstants.INTERNAL_SYSTEM_ROLE_PREFIX)) {
String errorMessage = String.format("Invalid role name: %s. Role names with the prefix: %s, is not allowed"
+ " to be created from externally in the system.", roleName,
UserCoreConstants.INTERNAL_SYSTEM_ROLE_PREFIX);
throw new IdentityRoleManagementClientException(INVALID_REQUEST.getCode(), errorMessage);
}
if (isDomainSeparatorPresent(roleName)) {
// SCIM2 API only adds roles to the internal domain.
throw new IdentityRoleManagementClientException(INVALID_REQUEST.getCode(), "Invalid character: "
+ UserCoreConstants.DOMAIN_SEPARATOR + " contains in the role name: " + roleName + ".");
}
List<RoleManagementListener> roleManagementListenerList = RoleManagementServiceComponentHolder.getInstance()
.getRoleManagementListenerList();
for (RoleManagementListener roleManagementListener : roleManagementListenerList) {
if (roleManagementListener.isEnable()) {
roleManagementListener.preAddRole(roleName, userList, groupList,
permissions, audience, audienceId, tenantDomain);
try {
if (StringUtils.startsWithIgnoreCase(roleName, UserCoreConstants.INTERNAL_SYSTEM_ROLE_PREFIX)) {
String errorMessage = String.format("Invalid role name: %s. Role names with the prefix: %s, is not " +
"allowed to be created from externally in the system.", roleName,
UserCoreConstants.INTERNAL_SYSTEM_ROLE_PREFIX);
throw new IdentityRoleManagementClientException(INVALID_REQUEST.getCode(), errorMessage);
}
if (isDomainSeparatorPresent(roleName)) {
// SCIM2 API only adds roles to the internal domain.
throw new IdentityRoleManagementClientException(INVALID_REQUEST.getCode(), "Invalid character: "
+ UserCoreConstants.DOMAIN_SEPARATOR + " contains in the role name: " + roleName + ".");
}
List<RoleManagementListener> roleManagementListenerList = RoleManagementServiceComponentHolder.getInstance()
.getRoleManagementListenerList();
for (RoleManagementListener roleManagementListener : roleManagementListenerList) {
if (roleManagementListener.isEnable()) {
roleManagementListener.preAddRole(roleName, userList, groupList,
permissions, audience, audienceId, tenantDomain);
}
}
}

RoleManagementEventPublisherProxy roleManagementEventPublisherProxy = RoleManagementEventPublisherProxy
.getInstance();
roleManagementEventPublisherProxy.publishPreAddRoleWithException(roleName, userList, groupList, permissions,
audience, audienceId, tenantDomain);
RoleManagementEventPublisherProxy roleManagementEventPublisherProxy = RoleManagementEventPublisherProxy
.getInstance();
roleManagementEventPublisherProxy.publishPreAddRoleWithException(roleName, userList, groupList, permissions,
audience, audienceId, tenantDomain);

// Validate audience.
if (StringUtils.isNotEmpty(audience)) {
if (!(ORGANIZATION.equalsIgnoreCase(audience) || APPLICATION.equalsIgnoreCase(audience))) {
throw new IdentityRoleManagementClientException(INVALID_AUDIENCE.getCode(), "Invalid role audience");
}
if (ORGANIZATION.equalsIgnoreCase(audience)) {
validateOrganizationRoleAudience(audienceId, tenantDomain);
// Validate audience.
if (StringUtils.isNotEmpty(audience)) {
if (!(ORGANIZATION.equalsIgnoreCase(audience) || APPLICATION.equalsIgnoreCase(audience))) {
throw new IdentityRoleManagementClientException(INVALID_AUDIENCE.getCode(),
"Invalid role audience");
}
if (ORGANIZATION.equalsIgnoreCase(audience)) {
validateOrganizationRoleAudience(audienceId, tenantDomain);
audience = ORGANIZATION;
}
if (APPLICATION.equalsIgnoreCase(audience)) {
// audience validation done using listener.
audience = APPLICATION;
}
} else {
audience = ORGANIZATION;
audienceId = getOrganizationIdByTenantDomain(tenantDomain);
}
validatePermissions(permissions, audience, audienceId, tenantDomain);
RoleBasicInfo roleBasicInfo = roleDAO.addRole(roleName, userList, groupList, permissions, audience,
audienceId, tenantDomain);
roleManagementEventPublisherProxy.publishPostAddRole(roleBasicInfo.getId(), roleName, userList, groupList,
permissions, audience, audienceId, tenantDomain);
if (log.isDebugEnabled()) {
log.debug(String.format("%s added role of name : %s successfully.", getUser(tenantDomain), roleName));
}
RoleBasicInfo role = roleDAO.getRoleBasicInfoById(roleBasicInfo.getId(), tenantDomain);
for (RoleManagementListener roleManagementListener : roleManagementListenerList) {
if (roleManagementListener.isEnable()) {
roleManagementListener.postAddRole(role, roleName, userList,
groupList, permissions, audience, audienceId, tenantDomain);
}
}
if (APPLICATION.equalsIgnoreCase(audience)) {
// audience validation done using listener.
audience = APPLICATION;
return role;
} finally {
if (IdentityUtil.threadLocalProperties.get().get(IS_FRAGMENT_APP) != null) {
IdentityUtil.threadLocalProperties.get().remove(IS_FRAGMENT_APP);
}
} else {
audience = ORGANIZATION;
audienceId = getOrganizationIdByTenantDomain(tenantDomain);
}
validatePermissions(permissions, audience, audienceId, tenantDomain);
RoleBasicInfo roleBasicInfo = roleDAO.addRole(roleName, userList, groupList, permissions, audience, audienceId,
tenantDomain);
roleManagementEventPublisherProxy.publishPostAddRole(roleBasicInfo.getId(), roleName, userList, groupList,
permissions, audience, audienceId, tenantDomain);
if (log.isDebugEnabled()) {
log.debug(String.format("%s added role of name : %s successfully.", getUser(tenantDomain), roleName));
}
RoleBasicInfo role = roleDAO.getRoleBasicInfoById(roleBasicInfo.getId(), tenantDomain);
for (RoleManagementListener roleManagementListener : roleManagementListenerList) {
if (roleManagementListener.isEnable()) {
roleManagementListener.postAddRole(role, roleName, userList,
groupList, permissions, audience, audienceId, tenantDomain);
}
}
return role;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ public class RoleDAOImpl implements RoleDAO {
private static final String GROUPS = "groups";
private static final String PERMISSIONS = "permissions";
private static final String ASSOCIATED_APPLICATIONS = "associatedApplications";
private static final String IS_FRAGMENT_APP = "isFragmentApp";

@Override
public RoleBasicInfo addRole(String roleName, List<String> userList, List<String> groupList,
Expand Down Expand Up @@ -1609,8 +1610,8 @@ private void addRoleInfo(String roleId, String roleName, List<Permission> permis
try {
addRoleID(roleId, roleName, audienceRefId, tenantDomain, connection);
addPermissions(roleId, permissions, tenantDomain, connection);

if (APPLICATION.equals(audience) && !isOrganization(tenantDomain)) {
if (APPLICATION.equals(audience) && !isFragmentApp()) {
addAppRoleAssociation(roleId, audienceId, connection);
}
IdentityDatabaseUtil.commitTransaction(connection);
Expand All @@ -1626,6 +1627,23 @@ private void addRoleInfo(String roleId, String roleName, List<Permission> permis
}
}

/**
* Check whether the corresponding application is a fragment application. This check is using a thread local
* property which is set from the default role management listener.
*
* @return True if the application is a fragment application.
*/
private boolean isFragmentApp() {

if (IdentityUtil.threadLocalProperties.get().get(IS_FRAGMENT_APP) != null) {
boolean isFragmentApp = Boolean.parseBoolean(IdentityUtil.threadLocalProperties.get().
get(IS_FRAGMENT_APP).toString());
IdentityUtil.threadLocalProperties.get().remove(IS_FRAGMENT_APP);
return isFragmentApp;
}
return false;
}

@Override
public boolean isSharedRole(String roleId, String tenantDomain) throws IdentityRoleManagementException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ public class RoleDAOTest {
private static final String UPDATED_SHARED_ORG_ROLE_NAME = "new-sharing-org-role-001";
private static final String SUB_ORG_ROLE_NAME = "sub-org-role-200";
private static final String MOCKED_EXCEPTION = "Mocked Exception";
private static final String IS_FRAGMENT_APP = "isFragmentApp";
private static final String SHARED_APP_ROLE_NAME = "shared-app-role-name-01";
private static final String SHARED_APP_ID = "shared-app-id";

private static Map<String, BasicDataSource> dataSourceMap = new HashMap<>();
private List<String> userNamesList = new ArrayList<>();
Expand Down Expand Up @@ -242,6 +245,34 @@ public void testAddAppRole() throws Exception {
SAMPLE_TENANT_DOMAIN));
}

@Test
public void testAddAppRoleToFragmentApp() throws Exception {

RoleDAOImpl roleDAO = spy(new RoleDAOImpl());
mockCacheClearing(roleDAO);
identityDatabaseUtil.when(() -> IdentityDatabaseUtil.getUserDBConnection(anyBoolean()))
.thenAnswer(invocation -> getConnection());
identityDatabaseUtil.when(() -> IdentityDatabaseUtil.getDBConnection(anyBoolean()))
.thenAnswer(invocation -> getConnection());
identityUtil.when(IdentityUtil::getPrimaryDomainName).thenReturn(USER_DOMAIN_PRIMARY);
identityUtil.when(() -> IdentityUtil.extractDomainFromName(anyString())).thenCallRealMethod();
identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(SAMPLE_TENANT_ID);
// Mock the threadLocalProperties static variable.
Map<String, Object> mockThreadLocalProperties = new HashMap<>();
mockThreadLocalProperties.put(IS_FRAGMENT_APP, Boolean.TRUE.toString());
IdentityUtil.threadLocalProperties.set(mockThreadLocalProperties);
// Add role to a fragment application. WHen doing this we need to stop adding the role to the
// application mapping.
RoleBasicInfo sharedRoleBasicInfo = addRole(SHARED_APP_ROLE_NAME, APPLICATION_AUD, SHARED_APP_ID, roleDAO);
List<String> appIDs = roleDAO.getAssociatedApplicationIdsByRoleId(sharedRoleBasicInfo.getId(),
SAMPLE_TENANT_DOMAIN);
// The role should not be associated with any application.
assertTrue(appIDs.isEmpty());
// Remove the mocked threadLocalProperties.
Map<String, Object> threadLocalProperties = new HashMap<>();
IdentityUtil.threadLocalProperties.set(threadLocalProperties);
}

@Test
public void testAddRoles() throws Exception {

Expand Down

0 comments on commit f744960

Please sign in to comment.